diff options
author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2021-02-27 05:49:55 +0000 |
---|---|---|
committer | android-build-prod (mdb) <android-build-team-robot@google.com> | 2021-02-27 05:49:55 +0000 |
commit | 0d8be8af319b1c2d59b5d75a49a7eaf98de5fd8d (patch) | |
tree | cdf8cf856b8b694d560e88ba23590539c76c6e4f | |
parent | 6f12baac0b877d2d3ca91035d645507310466410 (diff) | |
parent | 47df6daa2cea78e737c171aa9dda5fdd5353f87a (diff) | |
download | spirv-tools-busytown-mac1010-release.tar.gz |
Snap for 7174163 from 47df6daa2cea78e737c171aa9dda5fdd5353f87a to busytown-mac1010-releasebusytown-mac1010-release
Change-Id: I87536067a8a8fba3327e7eee23bd2bda7af373c1
148 files changed, 5632 insertions, 7656 deletions
@@ -93,7 +93,6 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/dead_branch_elim_pass.cpp \ source/opt/dead_insert_elim_pass.cpp \ source/opt/dead_variable_elimination.cpp \ - source/opt/decompose_initialized_variables_pass.cpp \ source/opt/decoration_manager.cpp \ source/opt/debug_info_manager.cpp \ source/opt/def_use_manager.cpp \ @@ -112,7 +111,6 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/fold_spec_constant_op_and_composite_pass.cpp \ source/opt/freeze_spec_constant_value_pass.cpp \ source/opt/function.cpp \ - source/opt/generate_webgpu_initializers_pass.cpp \ source/opt/graphics_robust_access_pass.cpp \ source/opt/if_conversion.cpp \ source/opt/inline_pass.cpp \ @@ -126,7 +124,6 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/instrument_pass.cpp \ source/opt/ir_context.cpp \ source/opt/ir_loader.cpp \ - source/opt/legalize_vector_shuffle_pass.cpp \ source/opt/licm_pass.cpp \ source/opt/local_access_chain_convert_pass.cpp \ source/opt/local_redundancy_elimination.cpp \ @@ -161,10 +158,8 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/scalar_replacement_pass.cpp \ source/opt/set_spec_constant_default_value_pass.cpp \ source/opt/simplification_pass.cpp \ - source/opt/split_invalid_unreachable_pass.cpp \ source/opt/ssa_rewrite_pass.cpp \ source/opt/strength_reduction_pass.cpp \ - source/opt/strip_atomic_counter_memory_pass.cpp \ source/opt/strip_debug_info_pass.cpp \ source/opt/strip_reflect_info_pass.cpp \ source/opt/struct_cfg_analysis.cpp \ @@ -539,8 +539,6 @@ static_library("spvtools_opt") { "source/opt/dead_insert_elim_pass.h", "source/opt/dead_variable_elimination.cpp", "source/opt/dead_variable_elimination.h", - "source/opt/decompose_initialized_variables_pass.cpp", - "source/opt/decompose_initialized_variables_pass.h", "source/opt/decoration_manager.cpp", "source/opt/decoration_manager.h", "source/opt/debug_info_manager.cpp", @@ -578,8 +576,6 @@ static_library("spvtools_opt") { "source/opt/freeze_spec_constant_value_pass.h", "source/opt/function.cpp", "source/opt/function.h", - "source/opt/generate_webgpu_initializers_pass.cpp", - "source/opt/generate_webgpu_initializers_pass.h", "source/opt/graphics_robust_access_pass.cpp", "source/opt/graphics_robust_access_pass.h", "source/opt/if_conversion.cpp", @@ -608,8 +604,6 @@ static_library("spvtools_opt") { "source/opt/ir_loader.cpp", "source/opt/ir_loader.h", "source/opt/iterator.h", - "source/opt/legalize_vector_shuffle_pass.cpp", - "source/opt/legalize_vector_shuffle_pass.h", "source/opt/licm_pass.cpp", "source/opt/licm_pass.h", "source/opt/local_access_chain_convert_pass.cpp", @@ -680,14 +674,10 @@ static_library("spvtools_opt") { "source/opt/set_spec_constant_default_value_pass.h", "source/opt/simplification_pass.cpp", "source/opt/simplification_pass.h", - "source/opt/split_invalid_unreachable_pass.cpp", - "source/opt/split_invalid_unreachable_pass.h", "source/opt/ssa_rewrite_pass.cpp", "source/opt/ssa_rewrite_pass.h", "source/opt/strength_reduction_pass.cpp", "source/opt/strength_reduction_pass.h", - "source/opt/strip_atomic_counter_memory_pass.cpp", - "source/opt/strip_atomic_counter_memory_pass.h", "source/opt/strip_debug_info_pass.cpp", "source/opt/strip_debug_info_pass.h", "source/opt/strip_reflect_info_pass.cpp", @@ -1,11 +1,65 @@ Revision history for SPIRV-Tools -v2020.6-dev 2020-12-02 +v2021.0-dev 2021-02-16 + - Start SPIRV-Tools v2021.0-dev + +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) @@ -6,7 +6,7 @@ vars = { 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', 'googletest_revision': '3af06fe1664d30f98de1e78c53a7087e842a2547', 're2_revision': 'ca11026a032ce2a3de4b3c389ee53d2bdc8794d6', - 'spirv_headers_revision': 'f027d53ded7e230e008d37c8b47ede7cd308e19d', + 'spirv_headers_revision': 'faa570afbc91ac73d594d787486bcf8f2df1ace0', } deps = { @@ -136,7 +136,6 @@ As of this writing, there are 67 transforms including examples such as: * Loop-invariant code motion * Loop unroll * Other - * Generate WebGPU initializers * Graphics robust access * Upgrade memory model to VulkanKHR diff --git a/android_test/jni/Application.mk b/android_test/jni/Application.mk index d7ccd349..4e664659 100644 --- a/android_test/jni/Application.mk +++ b/android_test/jni/Application.mk @@ -1,5 +1,5 @@ APP_ABI := all APP_BUILD_SCRIPT := Android.mk -APP_STL := gnustl_static +APP_STL := c++_static APP_PLATFORM := android-9 NDK_TOOLCHAIN_VERSION := 4.9 diff --git a/build_overrides/build.gni b/build_overrides/build.gni index 833fcd34..cf3d6b79 100644 --- a/build_overrides/build.gni +++ b/build_overrides/build.gni @@ -16,9 +16,6 @@ # Chromium specific targets in a client project's GN file etc. build_with_chromium = false -# Don't use Chromium's third_party/binutils. -linux_use_bundled_binutils_override = false - declare_args() { # Android 32-bit non-component, non-clang builds cannot have symbol_level=2 # due to 4GiB file size limit, see https://crbug.com/648948. diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp index 2b47a564..05a0472e 100644 --- a/include/spirv-tools/instrument.hpp +++ b/include/spirv-tools/instrument.hpp @@ -170,7 +170,10 @@ static const int kInstMaxOutCnt = kInstStageOutCnt + 4; static const int kInstErrorBindlessBounds = 0; static const int kInstErrorBindlessUninit = 1; static const int kInstErrorBuffAddrUnallocRef = 2; -static const int kInstErrorBindlessBuffOOB = 3; +// Deleted: static const int kInstErrorBindlessBuffOOB = 3; +// This comment will will remain for 2 releases to allow +// for the transition of all builds. Buffer OOB is +// generating the following four differentiated codes instead: static const int kInstErrorBuffOOBUniform = 4; static const int kInstErrorBuffOOBStorage = 5; static const int kInstErrorBuffOOBUniformTexel = 6; diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index a0114c3f..658c4dd5 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -260,6 +260,11 @@ typedef enum spv_operand_type_t { 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 + // This is a sentinel value, and does not represent an operand type. // It should come last. SPV_OPERAND_TYPE_NUM_OPERAND_TYPES, @@ -482,7 +487,7 @@ typedef enum { 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, // Work in progress WebGPU 1.0. + 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. @@ -620,6 +625,12 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout( 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( diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index 2018e461..e7e7fc7a 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp @@ -104,6 +104,12 @@ class ValidatorOptions { 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); diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 27352b25..1683d077 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -68,11 +68,6 @@ class Optimizer { // 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. - // - // For collections of passes that are meant to transform the input into - // another execution environment, then the source environment should be - // supplied. e.g. for VulkanToWebGPUPasses the environment should be - // SPV_ENV_VULKAN_1_1 not SPV_ENV_WEBGPU_0. explicit Optimizer(spv_target_env env); // Disables copy/move constructor/assignment operations. @@ -106,16 +101,6 @@ class Optimizer { // from time to time. Optimizer& RegisterSizePasses(); - // Registers passes that have been prescribed for converting from Vulkan to - // WebGPU. This sequence of passes is subject to constant review and will - // change from time to time. - Optimizer& RegisterVulkanToWebGPUPasses(); - - // Registers passes that have been prescribed for converting from WebGPU to - // Vulkan. This sequence of passes is subject to constant review and will - // change from time to time. - Optimizer& RegisterWebGPUToVulkanPasses(); - // Registers passes that attempt to legalize the generated code. // // Note: this recipe is specially designed for legalizing SPIR-V. It should be @@ -238,13 +223,6 @@ class Optimizer { // A null pass does nothing to the SPIR-V module to be optimized. Optimizer::PassToken CreateNullPass(); -// Creates a strip-atomic-counter-memory pass. -// A strip-atomic-counter-memory pass removes all usages of the -// AtomicCounterMemory bit in Memory Semantics bitmasks. This bit is a no-op in -// Vulkan, so isn't needed in that env. And the related capability is not -// allowed in WebGPU, so it is not allowed in that env. -Optimizer::PassToken CreateStripAtomicCounterMemoryPass(); - // Creates a strip-debug-info pass. // A strip-debug-info pass removes all debug instructions (as documented in // Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized. @@ -802,30 +780,11 @@ Optimizer::PassToken CreateUpgradeMemoryModelPass(); // where an instruction is moved into a more deeply nested construct. Optimizer::PassToken CreateCodeSinkingPass(); -// Create a pass to adds initializers for OpVariable calls that require them -// in WebGPU. Currently this pass naively initializes variables that are -// missing an initializer with a null value. In the future it may initialize -// variables to the first value stored in them, if that is a constant. -Optimizer::PassToken CreateGenerateWebGPUInitializersPass(); - // 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(); -// Create a pass to legalize OpVectorShuffle operands going into WebGPU. WebGPU -// forbids using 0xFFFFFFFF, which indicates an undefined result, so this pass -// converts those literals to 0. -Optimizer::PassToken CreateLegalizeVectorShufflePass(); - -// Create a pass to decompose initialized variables into a seperate variable -// declaration and an initial store. -Optimizer::PassToken CreateDecomposeInitializedVariablesPass(); - -// Create a pass to attempt to split up invalid unreachable merge-blocks and -// continue-targets to legalize for WebGPU. -Optimizer::PassToken CreateSplitInvalidUnreachablePass(); - // Creates a graphics robust access pass. // // This pass injects code to clamp indexed accesses to buffers and internal diff --git a/kokoro/android/build.sh b/kokoro/android/build.sh index c05c139a..22c9117a 100644..100755 --- a/kokoro/android/build.sh +++ b/kokoro/android/build.sh @@ -20,33 +20,5 @@ set -e # Display commands being run. set -x -BUILD_ROOT=$PWD -SRC=$PWD/github/SPIRV-Tools -TARGET_ARCH="armeabi-v7a with NEON" -export ANDROID_NDK=/opt/android-ndk-r15c - -# Get NINJA. -wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip -unzip -q ninja-linux.zip -export PATH="$PWD:$PATH" -git clone --depth=1 https://github.com/taka-no-me/android-cmake.git android-cmake -export TOOLCHAIN_PATH=$PWD/android-cmake/android.toolchain.cmake - - -cd $SRC -git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers -git clone https://github.com/google/googletest external/googletest -cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. -git clone --depth=1 https://github.com/google/effcee external/effcee -git clone --depth=1 https://github.com/google/re2 external/re2 - -mkdir build && cd $SRC/build - -# Invoke the build. -BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} -echo $(date): Starting build... -cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 -DCMAKE_BUILD_TYPE=Release -DANDROID_NATIVE_API_LEVEL=android-14 -DANDROID_ABI="armeabi-v7a with NEON" -DSPIRV_SKIP_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN_PATH -GNinja -DANDROID_NDK=$ANDROID_NDK .. - -echo $(date): Build everything... -ninja -echo $(date): Build completed. +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh ASAN clang cmake-android-ndk diff --git a/kokoro/linux-clang-asan/build.sh b/kokoro/linux-clang-asan/build.sh index 8f86e6ec..5cca362d 100644..100755 --- a/kokoro/linux-clang-asan/build.sh +++ b/kokoro/linux-clang-asan/build.sh @@ -21,4 +21,4 @@ set -e set -x SCRIPT_DIR=`dirname "$BASH_SOURCE"` -source $SCRIPT_DIR/../scripts/linux/build.sh ASAN clang +source $SCRIPT_DIR/../scripts/linux/build.sh ASAN clang cmake diff --git a/kokoro/linux-clang-debug/build.sh b/kokoro/linux-clang-debug/build.sh index 11b2968a..785a6e3b 100644..100755 --- a/kokoro/linux-clang-debug/build.sh +++ b/kokoro/linux-clang-debug/build.sh @@ -21,4 +21,4 @@ set -e set -x SCRIPT_DIR=`dirname "$BASH_SOURCE"` -source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG clang +source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG clang cmake diff --git a/kokoro/linux-clang-release-bazel/build.sh b/kokoro/linux-clang-release-bazel/build.sh index 05a9bbb4..238ef52b 100644..100755 --- a/kokoro/linux-clang-release-bazel/build.sh +++ b/kokoro/linux-clang-release-bazel/build.sh @@ -20,24 +20,5 @@ set -e # Display commands being run. set -x -CC=clang -CXX=clang++ -SRC=$PWD/github/SPIRV-Tools - -cd $SRC -git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers -git clone https://github.com/google/googletest external/googletest -cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. -git clone --depth=1 https://github.com/google/effcee external/effcee -git clone --depth=1 https://github.com/google/re2 external/re2 - -gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-linux-x86_64 . -chmod +x bazel-0.29.1-linux-x86_64 - -echo $(date): Build everything... -./bazel-0.29.1-linux-x86_64 build :all -echo $(date): Build completed. - -echo $(date): Starting bazel test... -./bazel-0.29.1-linux-x86_64 test :all -echo $(date): Bazel test completed. +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE clang bazel diff --git a/kokoro/linux-clang-release/build.sh b/kokoro/linux-clang-release/build.sh index 47643317..6a9e0136 100644..100755 --- a/kokoro/linux-clang-release/build.sh +++ b/kokoro/linux-clang-release/build.sh @@ -21,4 +21,4 @@ set -e set -x SCRIPT_DIR=`dirname "$BASH_SOURCE"` -source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE clang +source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE clang cmake diff --git a/kokoro/linux-gcc-debug/build.sh b/kokoro/linux-gcc-debug/build.sh index 3ef1e251..c60447e3 100644..100755 --- a/kokoro/linux-gcc-debug/build.sh +++ b/kokoro/linux-gcc-debug/build.sh @@ -21,4 +21,4 @@ set -e set -x SCRIPT_DIR=`dirname "$BASH_SOURCE"` -source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG gcc +source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG gcc cmake diff --git a/kokoro/linux-gcc-release/build.sh b/kokoro/linux-gcc-release/build.sh index 3e97d8d3..441ab728 100644..100755 --- a/kokoro/linux-gcc-release/build.sh +++ b/kokoro/linux-gcc-release/build.sh @@ -21,4 +21,4 @@ set -e set -x SCRIPT_DIR=`dirname "$BASH_SOURCE"` -source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc +source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc cmake diff --git a/kokoro/ndk-build/build.sh b/kokoro/ndk-build/build.sh index f1f167d8..89c55659 100644..100755 --- a/kokoro/ndk-build/build.sh +++ b/kokoro/ndk-build/build.sh @@ -20,38 +20,5 @@ set -e # Display commands being run. set -x -BUILD_ROOT=$PWD -SRC=$PWD/github/SPIRV-Tools - -# Get NINJA. -wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip -unzip -q ninja-linux.zip -export PATH="$PWD:$PATH" - -# NDK Path -export ANDROID_NDK=/opt/android-ndk-r15c - -# Get the dependencies. -cd $SRC -git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers -git clone https://github.com/google/googletest external/googletest -cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. -git clone --depth=1 https://github.com/google/effcee external/effcee -git clone --depth=1 https://github.com/google/re2 external/re2 - -mkdir build && cd $SRC/build -mkdir libs -mkdir app - -# Invoke the build. -BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} -echo $(date): Starting ndk-build ... -$ANDROID_NDK/ndk-build \ - -C $SRC/android_test \ - NDK_PROJECT_PATH=. \ - NDK_LIBS_OUT=./libs \ - NDK_APP_OUT=./app \ - -j8 - -echo $(date): ndk-build completed. - +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh ASAN clang android-ndk-build diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh new file mode 100755 index 00000000..ba216987 --- /dev/null +++ b/kokoro/scripts/linux/build-docker.sh @@ -0,0 +1,196 @@ +#!/bin/bash +# 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. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +. /bin/using.sh # Declare the bash `using` function for configuring toolchains. + +if [ $COMPILER = "clang" ]; then + using clang-10.0.0 +elif [ $COMPILER = "gcc" ]; then + using gcc-9 +fi + +cd $ROOT_DIR + +function clone_if_missing() { + url=$1 + dir=$2 + if [[ ! -d "$dir" ]]; then + git clone ${@:3} "$url" "$dir" + fi +} + +function clean_dir() { + dir=$1 + if [[ -d "$dir" ]]; then + rm -fr "$dir" + fi + mkdir "$dir" +} + +clone_if_missing https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers --depth=1 +clone_if_missing https://github.com/google/googletest external/googletest +pushd external/googletest; git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7; popd +clone_if_missing https://github.com/google/effcee external/effcee --depth=1 +clone_if_missing https://github.com/google/re2 external/re2 --depth=1 +clone_if_missing https://github.com/protocolbuffers/protobuf external/protobuf --branch v3.13.0 + +if [ $TOOL = "cmake" ]; then + using cmake-3.17.2 + using ninja-1.10.0 + + # Possible configurations are: + # ASAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW + BUILD_TYPE="Debug" + if [ $CONFIG = "RELEASE" ] || [ $CONFIG = "RELEASE_MINGW" ]; then + BUILD_TYPE="RelWithDebInfo" + fi + + SKIP_TESTS="False" + ADDITIONAL_CMAKE_FLAGS="" + if [ $CONFIG = "ASAN" ]; then + ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=address,bounds,null" + [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } + elif [ $CONFIG = "COVERAGE" ]; then + ADDITIONAL_CMAKE_FLAGS="-DENABLE_CODE_COVERAGE=ON" + SKIP_TESTS="True" + elif [ $CONFIG = "DEBUG_EXCEPTION" ]; then + ADDITIONAL_CMAKE_FLAGS="-DDISABLE_EXCEPTIONS=ON -DDISABLE_RTTI=ON" + elif [ $CONFIG = "RELEASE_MINGW" ]; then + ADDITIONAL_CMAKE_FLAGS="-Dgtest_disable_pthreads=ON -DCMAKE_TOOLCHAIN_FILE=$SRC/cmake/linux-mingw-toolchain.cmake" + SKIP_TESTS="True" + fi + + clean_dir "$ROOT_DIR/build" + cd "$ROOT_DIR/build" + + # Invoke the build. + BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} + echo $(date): Starting build... + cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 -GNinja -DCMAKE_INSTALL_PREFIX=$KOKORO_ARTIFACTS_DIR/install -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DRE2_BUILD_TESTING=OFF -DSPIRV_BUILD_FUZZER=ON $ADDITIONAL_CMAKE_FLAGS .. + + echo $(date): Build everything... + ninja + echo $(date): Build completed. + + if [ $CONFIG = "COVERAGE" ]; then + echo $(date): Check coverage... + ninja report-coverage + echo $(date): Check coverage completed. + fi + + echo $(date): Starting ctest... + if [ $SKIP_TESTS = "False" ]; then + ctest -j4 --output-on-failure --timeout 300 + fi + echo $(date): ctest completed. + + # Package the build. + ninja install + cd $KOKORO_ARTIFACTS_DIR + tar czf install.tgz install +elif [ $TOOL = "cmake-smoketest" ]; then + using cmake-3.17.2 + using ninja-1.10.0 + + # Get shaderc. + SHADERC_DIR=/tmp/shaderc + clean_dir "$SHADERC_DIR" + cd $SHADERC_DIR + git clone https://github.com/google/shaderc.git . + cd $SHADERC_DIR/third_party + + # Get shaderc dependencies. Link the appropriate SPIRV-Tools. + git clone https://github.com/google/googletest.git + git clone https://github.com/KhronosGroup/glslang.git + ln -s $ROOT_DIR spirv-tools + git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-headers + git clone https://github.com/google/re2 + git clone https://github.com/google/effcee + + cd $SHADERC_DIR + mkdir build + cd $SHADERC_DIR/build + + # Invoke the build. + echo $(date): Starting build... + cmake -GNinja -DRE2_BUILD_TESTING=OFF -DCMAKE_BUILD_TYPE="Release" .. + + echo $(date): Build glslang... + ninja glslangValidator + + echo $(date): Build everything... + ninja + echo $(date): Build completed. + + echo $(date): Check Shaderc for copyright notices... + ninja check-copyright + + echo $(date): Starting ctest... + ctest --output-on-failure -j4 + echo $(date): ctest completed. +elif [ $TOOL = "cmake-android-ndk" ]; then + using cmake-3.17.2 + using ndk-r21d + using ninja-1.10.0 + + clean_dir "$ROOT_DIR/build" + cd "$ROOT_DIR/build" + + echo $(date): Starting build... + cmake -DCMAKE_BUILD_TYPE=Release \ + -DANDROID_NATIVE_API_LEVEL=android-16 \ + -DANDROID_ABI="armeabi-v7a with NEON" \ + -DSPIRV_SKIP_TESTS=ON \ + -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ + -GNinja \ + -DANDROID_NDK=$ANDROID_NDK \ + .. + + echo $(date): Build everything... + ninja + echo $(date): Build completed. +elif [ $TOOL = "android-ndk-build" ]; then + using ndk-r21d + + clean_dir "$ROOT_DIR/build" + cd "$ROOT_DIR/build" + + echo $(date): Starting ndk-build ... + $ANDROID_NDK_HOME/ndk-build \ + -C $ROOT_DIR/android_test \ + NDK_PROJECT_PATH=. \ + NDK_LIBS_OUT=./libs \ + NDK_APP_OUT=./app \ + -j4 + + echo $(date): ndk-build completed. +elif [ $TOOL = "bazel" ]; then + using bazel-3.1.0 + + echo $(date): Build everything... + bazel build :all + echo $(date): Build completed. + + echo $(date): Starting bazel test... + bazel test :all + echo $(date): Bazel test completed. +fi diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh index 347f353a..4731ebdc 100644 --- a/kokoro/scripts/linux/build.sh +++ b/kokoro/scripts/linux/build.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2018 Google LLC. +# 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. @@ -17,89 +17,38 @@ # Fail on any error. set -e -# Display commands being run. -set -x -BUILD_ROOT=$PWD -SRC=$PWD/github/SPIRV-Tools +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +ROOT_DIR="$( cd "${SCRIPT_DIR}/../../.." >/dev/null 2>&1 && pwd )" + CONFIG=$1 COMPILER=$2 - -SKIP_TESTS="False" -BUILD_TYPE="Debug" - -CMAKE_C_CXX_COMPILER="" -if [ $COMPILER = "clang" ] -then - PATH=/usr/lib/llvm-3.8/bin:$PATH - CMAKE_C_CXX_COMPILER="-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++" -fi - -# Possible configurations are: -# ASAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW - -if [ $CONFIG = "RELEASE" ] || [ $CONFIG = "RELEASE_MINGW" ] -then - BUILD_TYPE="RelWithDebInfo" -fi - -ADDITIONAL_CMAKE_FLAGS="" -if [ $CONFIG = "ASAN" ] -then - ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=address,bounds,null" - [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } -elif [ $CONFIG = "COVERAGE" ] -then - ADDITIONAL_CMAKE_FLAGS="-DENABLE_CODE_COVERAGE=ON" - SKIP_TESTS="True" -elif [ $CONFIG = "DEBUG_EXCEPTION" ] -then - ADDITIONAL_CMAKE_FLAGS="-DDISABLE_EXCEPTIONS=ON -DDISABLE_RTTI=ON" -elif [ $CONFIG = "RELEASE_MINGW" ] -then - ADDITIONAL_CMAKE_FLAGS="-Dgtest_disable_pthreads=ON -DCMAKE_TOOLCHAIN_FILE=$SRC/cmake/linux-mingw-toolchain.cmake" - SKIP_TESTS="True" -fi - -# Get NINJA. -wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip -unzip -q ninja-linux.zip -export PATH="$PWD:$PATH" - -cd $SRC -git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers -git clone https://github.com/google/googletest external/googletest -cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. -git clone --depth=1 https://github.com/google/effcee external/effcee -git clone --depth=1 https://github.com/google/re2 external/re2 -git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf - -mkdir build && cd $SRC/build - -# Invoke the build. +TOOL=$3 BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} -echo $(date): Starting build... -cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 -GNinja -DCMAKE_INSTALL_PREFIX=$KOKORO_ARTIFACTS_DIR/install -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DRE2_BUILD_TESTING=OFF -DSPIRV_BUILD_FUZZER=ON $ADDITIONAL_CMAKE_FLAGS $CMAKE_C_CXX_COMPILER .. - -echo $(date): Build everything... -ninja -echo $(date): Build completed. - -if [ $CONFIG = "COVERAGE" ] -then - echo $(date): Check coverage... - ninja report-coverage - echo $(date): Check coverage completed. -fi - -echo $(date): Starting ctest... -if [ $SKIP_TESTS = "False" ] -then - ctest -j4 --output-on-failure --timeout 300 -fi -echo $(date): ctest completed. -# Package the build. -ninja install -cd $KOKORO_ARTIFACTS_DIR -tar czf install.tgz install +docker run --rm -i \ + --volume "${ROOT_DIR}:${ROOT_DIR}" \ + --volume "${KOKORO_ARTIFACTS_DIR}:${KOKORO_ARTIFACTS_DIR}" \ + --workdir "${ROOT_DIR}" \ + --env SCRIPT_DIR=${SCRIPT_DIR} \ + --env ROOT_DIR=${ROOT_DIR} \ + --env CONFIG=${CONFIG} \ + --env COMPILER=${COMPILER} \ + --env TOOL=${TOOL} \ + --env KOKORO_ARTIFACTS_DIR="${KOKORO_ARTIFACTS_DIR}" \ + --env BUILD_SHA="${BUILD_SHA}" \ + --entrypoint "${SCRIPT_DIR}/build-docker.sh" \ + "gcr.io/shaderc-build/radial-build:latest" + + +# chown the given directory to the current user, if it exists. +# Docker creates files with the root user - this can upset the Kokoro artifact copier. +function chown_dir() { + dir=$1 + if [[ -d "$dir" ]]; then + sudo chown -R "$(id -u):$(id -g)" "$dir" + fi +} + +chown_dir "${ROOT_DIR}/build" +chown_dir "${ROOT_DIR}/external" diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat index fa7a71a0..a4ce792a 100644 --- a/kokoro/scripts/windows/build.bat +++ b/kokoro/scripts/windows/build.bat @@ -22,7 +22,7 @@ set BUILD_TYPE=%1 set VS_VERSION=%2 :: Force usage of python 3.6 -set PATH=C:\python36;%PATH% +set PATH=C:\python36;"C:\Program Files\CMake\bin";%PATH% cd %SRC% git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers diff --git a/kokoro/shaderc-smoketest/build.sh b/kokoro/shaderc-smoketest/build.sh index 0856c9b2..60c816d4 100644..100755 --- a/kokoro/shaderc-smoketest/build.sh +++ b/kokoro/shaderc-smoketest/build.sh @@ -18,54 +18,5 @@ set -e # Display commands being run. set -x -BUILD_ROOT=$PWD -GITHUB_DIR=$BUILD_ROOT/github - -SKIP_TESTS="False" -BUILD_TYPE="Release" - -# Get NINJA. -wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip -unzip -q ninja-linux.zip -export PATH="$PWD:$PATH" - -# Get shaderc. -cd $GITHUB_DIR -git clone https://github.com/google/shaderc.git -SHADERC_DIR=$GITHUB_DIR/shaderc -cd $SHADERC_DIR/third_party - -# Get shaderc dependencies. Link the appropriate SPIRV-Tools. -git clone https://github.com/google/googletest.git -git clone https://github.com/KhronosGroup/glslang.git -ln -s $GITHUB_DIR/SPIRV-Tools spirv-tools -git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-headers -git clone https://github.com/google/re2 -git clone https://github.com/google/effcee - -cd $SHADERC_DIR -mkdir build -cd $SHADERC_DIR/build - -# Invoke the build. -BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} -echo $(date): Starting build... -cmake -GNinja -DRE2_BUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=$BUILD_TYPE .. - -echo $(date): Build glslang... -ninja glslangValidator - -echo $(date): Build everything... -ninja -echo $(date): Build completed. - -echo $(date): Check Shaderc for copyright notices... -ninja check-copyright - -echo $(date): Starting ctest... -if [ $SKIP_TESTS = "False" ] -then - ctest --output-on-failure -j4 -fi -echo $(date): ctest completed. - +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc cmake-smoketest diff --git a/source/binary.cpp b/source/binary.cpp index 75a997d3..7448721b 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -655,7 +655,9 @@ spv_result_t Parser::parseOperand(size_t inst_offset, 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_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + case SPV_OPERAND_TYPE_FPDENORM_MODE: + case SPV_OPERAND_TYPE_FPOPERATION_MODE: { // A single word that is a plain enum value. // Map an optional operand type to its corresponding concrete type. diff --git a/source/disassemble.cpp b/source/disassemble.cpp index e7632512..966a59c7 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp @@ -326,7 +326,9 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, 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_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + case SPV_OPERAND_TYPE_FPDENORM_MODE: + case SPV_OPERAND_TYPE_FPOPERATION_MODE: { spv_operand_desc entry; if (grammar_.lookupOperand(operand.type, word, &entry)) assert(false && "should have caught this earlier"); diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 3471ebe0..795cb0f3 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -89,7 +89,6 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_VULKAN_1_1: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: - case SPV_ENV_WEBGPU_0: case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp index 07758cd3..2ac6259d 100644 --- a/source/fuzz/transformation_duplicate_region_with_selection.cpp +++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -209,7 +209,7 @@ bool TransformationDuplicateRegionWithSelection::IsApplicable( return false; } } else { - auto duplicate_label = original_label_to_duplicate_label[block->id()]; + 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( @@ -217,7 +217,7 @@ bool TransformationDuplicateRegionWithSelection::IsApplicable( return false; } } - for (auto instr : *block) { + for (auto& instr : *block) { if (!instr.HasResultId()) { continue; } @@ -228,7 +228,7 @@ bool TransformationDuplicateRegionWithSelection::IsApplicable( return false; } } else { - auto duplicate_id = original_id_to_duplicate_id[instr.result_id()]; + 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 || @@ -237,43 +237,48 @@ bool TransformationDuplicateRegionWithSelection::IsApplicable( return false; } } - if (&instr == &*exit_block->tail() || - fuzzerutil::IdIsAvailableBeforeInstruction( - ir_context, &*exit_block->tail(), instr.result_id())) { - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3787): - // Consider not adding OpPhi instructions for the pointers and - // sampled images which are unused after the region, so that the - // transformation could be still applicable. - - // Using pointers with OpPhi requires capability VariablePointers. - if (ir_context->get_def_use_mgr()->GetDef(instr.type_id())->opcode() == - SpvOpTypePointer && - !ir_context->get_feature_mgr()->HasCapability( - SpvCapabilityVariablePointers)) { - return false; - } - - // OpTypeSampledImage cannot be the result type of an OpPhi instruction. - if (ir_context->get_def_use_mgr()->GetDef(instr.type_id())->opcode() == - SpvOpTypeSampledImage) { - return false; - } - - // 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()) { + // 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 { - auto phi_id = original_id_to_phi_id[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; + // 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; + } } } } @@ -329,7 +334,7 @@ void TransformationDuplicateRegionWithSelection::Apply( {block->id(), transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); } - for (auto instr : *block) { + for (auto& instr : *block) { if (!instr.HasResultId()) { continue; } @@ -338,9 +343,8 @@ void TransformationDuplicateRegionWithSelection::Apply( {instr.result_id(), transformation_context->GetOverflowIdSource() ->GetNextOverflowId()}); } - if (&instr == &*exit_block->tail() || - fuzzerutil::IdIsAvailableBeforeInstruction( - ir_context, &*exit_block->tail(), instr.result_id())) { + 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() @@ -414,12 +418,12 @@ void TransformationDuplicateRegionWithSelection::Apply( } fuzzerutil::UpdateModuleIdBound( - ir_context, original_label_to_duplicate_label[block->id()]); + ir_context, original_label_to_duplicate_label.at(block->id())); std::unique_ptr<opt::BasicBlock> duplicated_block = MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>( ir_context, SpvOpLabel, 0, - original_label_to_duplicate_label[block->id()], + original_label_to_duplicate_label.at(block->id()), opt::Instruction::OperandList())); for (auto& instr : *block) { @@ -444,8 +448,10 @@ void TransformationDuplicateRegionWithSelection::Apply( duplicated_block->AddInstruction( std::unique_ptr<opt::Instruction>(cloned_instr)); - fuzzerutil::UpdateModuleIdBound( - ir_context, original_id_to_duplicate_id[instr.result_id()]); + 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|. @@ -456,8 +462,7 @@ void TransformationDuplicateRegionWithSelection::Apply( 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); - } - if (original_label_to_duplicate_label.count(*op) != 0) { + } else if (original_label_to_duplicate_label.count(*op) != 0) { *op = original_label_to_duplicate_label.at(*op); } }); @@ -484,26 +489,27 @@ void TransformationDuplicateRegionWithSelection::Apply( for (auto& block : region_blocks) { for (auto& instr : *block) { - if (instr.result_id() != 0 && - (&instr == &*exit_block->tail() || - fuzzerutil::IdIsAvailableBeforeInstruction( - ir_context, &*exit_block->tail(), instr.result_id()))) { - // Add the OpPhi instruction for every result id that is - // available at the end of the region (the last instruction - // of the |exit_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<opt::Instruction>( ir_context, SpvOpPhi, instr.type_id(), - original_id_to_phi_id[instr.result_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[instr.result_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[instr.result_id()]); + 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 @@ -544,7 +550,8 @@ void TransformationDuplicateRegionWithSelection::Apply( {{SPV_OPERAND_TYPE_ID, {message_.condition_id()}}, {SPV_OPERAND_TYPE_ID, {message_.entry_block_id()}}, {SPV_OPERAND_TYPE_ID, - {original_label_to_duplicate_label[message_.entry_block_id()]}}}))); + {original_label_to_duplicate_label.at( + message_.entry_block_id())}}}))); // Move the terminator of |exit_block| to the end of // |merge_block|. @@ -678,5 +685,38 @@ TransformationDuplicateRegionWithSelection::GetFreshIds() const { 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() == SpvOpTypeVoid) { + return false; + } + + // Using pointers with OpPhi requires capability VariablePointers. + if (instr_type->opcode() == SpvOpTypePointer && + !ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers)) { + return false; + } + + // OpTypeSampledImage cannot be the result type of an OpPhi instruction. + if (instr_type->opcode() == SpvOpTypeSampledImage) { + return false; + } + return true; +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/transformation_duplicate_region_with_selection.h b/source/fuzz/transformation_duplicate_region_with_selection.h index d6f0ad9a..a2b9a433 100644 --- a/source/fuzz/transformation_duplicate_region_with_selection.h +++ b/source/fuzz/transformation_duplicate_region_with_selection.h @@ -66,6 +66,17 @@ class TransformationDuplicateRegionWithSelection : public Transformation { 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<uint32_t> GetFreshIds() const override; protobufs::Transformation ToMessage() const override; diff --git a/source/libspirv.cpp b/source/libspirv.cpp index a1ed11d3..0bc09350 100644 --- a/source/libspirv.cpp +++ b/source/libspirv.cpp @@ -14,8 +14,8 @@ #include "spirv-tools/libspirv.hpp" +#include <cassert> #include <iostream> - #include <string> #include <utility> #include <vector> @@ -60,7 +60,9 @@ struct SpirvTools::Impl { spv_context context; // C interface context object. }; -SpirvTools::SpirvTools(spv_target_env env) : impl_(new Impl(env)) {} +SpirvTools::SpirvTools(spv_target_env env) : impl_(new Impl(env)) { + assert(env != SPV_ENV_WEBGPU_0); +} SpirvTools::~SpirvTools() {} diff --git a/source/link/linker.cpp b/source/link/linker.cpp index da6f0a78..8da4a98d 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -676,14 +676,15 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries, if (schema != 0u) { position.index = 4u; return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) - << "Schema is non-zero for module " << i << "."; + << "Schema is non-zero for module " << i + 1 << "."; } std::unique_ptr<IRContext> 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 a module out of " << ir_contexts.size() << "."; + << "Failed to build module " << i + 1 << " out of " << num_binaries + << "."; modules.push_back(ir_context->module()); ir_contexts.push_back(std::move(ir_context)); } diff --git a/source/opcode.cpp b/source/opcode.cpp index c80e3a00..d87e8287 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -444,15 +444,32 @@ bool spvOpcodeIsReturn(SpvOp opcode) { } } +bool spvOpcodeIsAbort(SpvOp opcode) { + switch (opcode) { + case SpvOpKill: + case SpvOpUnreachable: + case SpvOpTerminateInvocation: + case SpvOpTerminateRayKHR: + case SpvOpIgnoreIntersectionKHR: + return true; + default: + return false; + } +} + bool spvOpcodeIsReturnOrAbort(SpvOp opcode) { - return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill || - opcode == SpvOpUnreachable || opcode == SpvOpTerminateInvocation; + return spvOpcodeIsReturn(opcode) || spvOpcodeIsAbort(opcode); } bool spvOpcodeIsBlockTerminator(SpvOp opcode) { return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode); } +bool spvOpcodeTerminatesExecution(SpvOp opcode) { + return opcode == SpvOpKill || opcode == SpvOpTerminateInvocation || + opcode == SpvOpTerminateRayKHR || opcode == SpvOpIgnoreIntersectionKHR; +} + bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) { switch (opcode) { case SpvOpTypeImage: diff --git a/source/opcode.h b/source/opcode.h index 3702cb35..c8525a25 100644 --- a/source/opcode.h +++ b/source/opcode.h @@ -110,10 +110,18 @@ bool spvOpcodeIsBranch(SpvOp opcode); // Returns true if the given opcode is a return instruction. bool spvOpcodeIsReturn(SpvOp opcode); +// Returns true if the given opcode aborts execution. +bool spvOpcodeIsAbort(SpvOp opcode); + // Returns true if the given opcode is a return instruction or it aborts // execution. bool spvOpcodeIsReturnOrAbort(SpvOp opcode); +// Returns true if the given opcode is a kill instruction or it terminates +// execution. Note that branches, returns, and unreachables do not terminate +// execution. +bool spvOpcodeTerminatesExecution(SpvOp opcode); + // Returns true if the given opcode is a basic block terminator. bool spvOpcodeIsBlockTerminator(SpvOp opcode); diff --git a/source/operand.cpp b/source/operand.cpp index d4b64a8b..5a69fb24 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -24,6 +24,7 @@ #include "DebugInfo.h" #include "OpenCLDebugInfo100.h" #include "source/macro.h" +#include "source/opcode.h" #include "source/spirv_constant.h" #include "source/spirv_target_env.h" @@ -264,6 +265,11 @@ const char* spvOperandTypeStr(spv_operand_type_t type) { 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_NONE: return "NONE"; default: @@ -347,6 +353,8 @@ bool spvOperandIsConcrete(spv_operand_type_t 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: return true; default: break; @@ -491,6 +499,11 @@ bool spvIsInIdType(spv_operand_type_t type) { std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction( SpvOp opcode) { std::function<bool(unsigned index)> out; + if (spvOpcodeGeneratesType(opcode)) { + // All types can use forward pointers. + out = [](unsigned) { return true; }; + return out; + } switch (opcode) { case SpvOpExecutionMode: case SpvOpExecutionModeId: @@ -503,7 +516,6 @@ std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction( case SpvOpDecorateId: case SpvOpDecorateStringGOOGLE: case SpvOpMemberDecorateStringGOOGLE: - case SpvOpTypeStruct: case SpvOpBranch: case SpvOpLoopMerge: out = [](unsigned) { return true; }; diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index f3ac5906..14a6bee7 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -32,7 +32,6 @@ set(SPIRV_TOOLS_OPT_SOURCES dead_branch_elim_pass.h dead_insert_elim_pass.h dead_variable_elimination.h - decompose_initialized_variables_pass.h decoration_manager.h debug_info_manager.h def_use_manager.h @@ -52,7 +51,6 @@ set(SPIRV_TOOLS_OPT_SOURCES fold_spec_constant_op_and_composite_pass.h freeze_spec_constant_value_pass.h function.h - generate_webgpu_initializers_pass.h graphics_robust_access_pass.h if_conversion.h inline_exhaustive_pass.h @@ -103,10 +101,8 @@ set(SPIRV_TOOLS_OPT_SOURCES scalar_replacement_pass.h set_spec_constant_default_value_pass.h simplification_pass.h - split_invalid_unreachable_pass.h ssa_rewrite_pass.h strength_reduction_pass.h - strip_atomic_counter_memory_pass.h strip_debug_info_pass.h strip_reflect_info_pass.h struct_cfg_analysis.h @@ -140,7 +136,6 @@ set(SPIRV_TOOLS_OPT_SOURCES dead_branch_elim_pass.cpp dead_insert_elim_pass.cpp dead_variable_elimination.cpp - decompose_initialized_variables_pass.cpp decoration_manager.cpp debug_info_manager.cpp def_use_manager.cpp @@ -160,7 +155,6 @@ set(SPIRV_TOOLS_OPT_SOURCES freeze_spec_constant_value_pass.cpp function.cpp graphics_robust_access_pass.cpp - generate_webgpu_initializers_pass.cpp if_conversion.cpp inline_exhaustive_pass.cpp inline_opaque_pass.cpp @@ -173,7 +167,6 @@ set(SPIRV_TOOLS_OPT_SOURCES instrument_pass.cpp ir_context.cpp ir_loader.cpp - legalize_vector_shuffle_pass.cpp licm_pass.cpp local_access_chain_convert_pass.cpp local_redundancy_elimination.cpp @@ -208,10 +201,8 @@ set(SPIRV_TOOLS_OPT_SOURCES scalar_replacement_pass.cpp set_spec_constant_default_value_pass.cpp simplification_pass.cpp - split_invalid_unreachable_pass.cpp ssa_rewrite_pass.cpp strength_reduction_pass.cpp - strip_atomic_counter_memory_pass.cpp strip_debug_info_pass.cpp strip_reflect_info_pass.cpp struct_cfg_analysis.cpp diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 94451a29..81b2232f 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -995,6 +995,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", "SPV_KHR_terminate_invocation", + "SPV_KHR_shader_clock", }); } diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp index b7e122c4..e82a744a 100644 --- a/source/opt/basic_block.cpp +++ b/source/opt/basic_block.cpp @@ -230,7 +230,7 @@ std::string BasicBlock::PrettyPrint(uint32_t options) const { std::ostringstream str; ForEachInst([&str, options](const Instruction* inst) { str << inst->PrettyPrint(options); - if (!IsTerminatorInst(inst->opcode())) { + if (!spvOpcodeIsBlockTerminator(inst->opcode())) { str << std::endl; } }); diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp index 5022e1b6..6b3b540a 100644 --- a/source/opt/convert_to_half_pass.cpp +++ b/source/opt/convert_to_half_pass.cpp @@ -147,8 +147,8 @@ bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) { return true; } -void ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) { - context()->get_decoration_mgr()->RemoveDecorationsFrom( +bool ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) { + return context()->get_decoration_mgr()->RemoveDecorationsFrom( id, [](const Instruction& dec) { if (dec.opcode() == SpvOpDecorate && dec.GetSingleWordInOperand(1u) == SpvDecorationRelaxedPrecision) @@ -344,10 +344,14 @@ Pass::Status ConvertToHalfPass::ProcessImpl() { // If modified, make sure module has Float16 capability if (modified) context()->AddCapability(SpvCapabilityFloat16); // Remove all RelaxedPrecision decorations from instructions and globals - for (auto c_id : relaxed_ids_set_) RemoveRelaxedDecoration(c_id); + 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) RemoveRelaxedDecoration(v_id); + if (v_id != 0) { + modified |= RemoveRelaxedDecoration(v_id); + } } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h index 143aebfa..b647dd4a 100644 --- a/source/opt/convert_to_half_pass.h +++ b/source/opt/convert_to_half_pass.h @@ -74,7 +74,7 @@ class ConvertToHalfPass : public Pass { void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst); // Remove RelaxedPrecision decoration of |id|. - void RemoveRelaxedDecoration(uint32_t 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 diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index 11b5131d..e782641f 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp @@ -500,14 +500,15 @@ bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible( insert_before->opcode() == SpvOpVariable) { insert_before = insert_before->NextNode(); } - modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, - insert_before) != nullptr; + 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* 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<Instruction> dbg_val(dbg_decl->Clone(context())); @@ -517,6 +518,7 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( 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); diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h index 92b38183..776e9baa 100644 --- a/source/opt/debug_info_manager.h +++ b/source/opt/debug_info_manager.h @@ -152,11 +152,14 @@ class DebugInfoManager { std::unordered_set<Instruction*>* invisible_decls); // Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before - // |insert_before|. The new DebugValue has the same line, scope, and - // operands with DebugDeclare but it uses |value_id| for value. Returns - // the added DebugValue, or nullptr if it does not add a DebugValue. + // |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* insert_before, + Instruction* scope_and_line); // Erases |instr| from data structures of this class. void ClearDebugInfo(Instruction* instr); diff --git a/source/opt/decompose_initialized_variables_pass.cpp b/source/opt/decompose_initialized_variables_pass.cpp deleted file mode 100644 index 875bf7e8..00000000 --- a/source/opt/decompose_initialized_variables_pass.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// 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/decompose_initialized_variables_pass.h" - -#include "source/opt/ir_context.h" - -namespace spvtools { -namespace opt { - -using inst_iterator = InstructionList::iterator; - -namespace { - -bool HasInitializer(Instruction* inst) { - if (inst->opcode() != SpvOpVariable) return false; - if (inst->NumOperands() < 4) return false; - - return true; -} - -} // namespace - -Pass::Status DecomposeInitializedVariablesPass::Process() { - auto* module = context()->module(); - std::unordered_set<Instruction*> changed; - - std::vector<std::tuple<uint32_t, uint32_t>> global_stores; - for (auto iter = module->types_values_begin(); - iter != module->types_values_end(); ++iter) { - Instruction* inst = &(*iter); - if (!HasInitializer(inst)) continue; - - auto var_id = inst->result_id(); - auto val_id = inst->GetOperand(3).words[0]; - global_stores.push_back(std::make_tuple(var_id, val_id)); - iter->RemoveOperand(3); - changed.insert(&*iter); - } - - std::unordered_set<uint32_t> entry_ids; - for (auto entry = module->entry_points().begin(); - entry != module->entry_points().end(); ++entry) { - entry_ids.insert(entry->GetSingleWordInOperand(1)); - } - - for (auto func = module->begin(); func != module->end(); ++func) { - std::vector<Instruction*> function_stores; - auto first_block = func->entry().get(); - inst_iterator insert_point = first_block->begin(); - for (auto iter = first_block->begin(); - iter != first_block->end() && iter->opcode() == SpvOpVariable; - ++iter) { - // For valid SPIRV-V, there is guaranteed to be at least one instruction - // after the OpVariable instructions. - insert_point = (*iter).NextNode(); - Instruction* inst = &(*iter); - if (!HasInitializer(inst)) continue; - - auto var_id = inst->result_id(); - auto val_id = inst->GetOperand(3).words[0]; - Instruction* store_inst = new Instruction( - context(), SpvOpStore, 0, 0, - {{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}}); - function_stores.push_back(store_inst); - iter->RemoveOperand(3); - changed.insert(&*iter); - } - - if (entry_ids.find(func->result_id()) != entry_ids.end()) { - for (auto store_ids : global_stores) { - uint32_t var_id; - uint32_t val_id; - std::tie(var_id, val_id) = store_ids; - auto* store_inst = new Instruction( - context(), SpvOpStore, 0, 0, - {{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}}); - context()->set_instr_block(store_inst, &*first_block); - first_block->AddInstruction(std::unique_ptr<Instruction>(store_inst)); - store_inst->InsertBefore(&*insert_point); - changed.insert(store_inst); - } - } - - for (auto store = function_stores.begin(); store != function_stores.end(); - ++store) { - context()->set_instr_block(*store, first_block); - (*store)->InsertBefore(&*insert_point); - changed.insert(*store); - } - } - - auto* def_use_mgr = get_def_use_mgr(); - for (auto* inst : changed) def_use_mgr->UpdateDefUse(inst); - - return !changed.empty() ? Pass::Status::SuccessWithChange - : Pass::Status::SuccessWithoutChange; -} - -} // namespace opt -} // namespace spvtools diff --git a/source/opt/decompose_initialized_variables_pass.h b/source/opt/decompose_initialized_variables_pass.h deleted file mode 100644 index c0bd35e7..00000000 --- a/source/opt/decompose_initialized_variables_pass.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ -#define SOURCE_OPT_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ - -#include "source/opt/ir_context.h" -#include "source/opt/module.h" -#include "source/opt/pass.h" - -namespace spvtools { -namespace opt { - -// Converts variable declartions with initializers into seperate declaration and -// assignment statements. This is done due to known issues with some Vulkan -// implementations' handling of initialized variables. -// -// Only decomposes variables with storage classes that are valid in Vulkan -// execution environments; Output, Private, and Function. -// Currently only Function is implemented. -class DecomposeInitializedVariablesPass : public Pass { - public: - const char* name() const override { - return "decompose-initialized-variables"; - } - 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_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp index 8b4aee58..4bf026ef 100644 --- a/source/opt/decoration_manager.cpp +++ b/source/opt/decoration_manager.cpp @@ -53,11 +53,12 @@ namespace spvtools { namespace opt { namespace analysis { -void DecorationManager::RemoveDecorationsFrom( +bool DecorationManager::RemoveDecorationsFrom( uint32_t id, std::function<bool(const Instruction&)> pred) { + bool was_modified = false; const auto ids_iter = id_to_decoration_insts_.find(id); if (ids_iter == id_to_decoration_insts_.end()) { - return; + return was_modified; } TargetData& decorations_info = ids_iter->second; @@ -99,7 +100,6 @@ void DecorationManager::RemoveDecorationsFrom( // Otherwise, remove |id| from the targets of |group_id| const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u; - bool was_modified = false; for (uint32_t i = 1u; i < inst->NumInOperands();) { if (inst->GetSingleWordInOperand(i) != id) { i += stride; @@ -155,6 +155,7 @@ void DecorationManager::RemoveDecorationsFrom( }), indirect_decorations.end()); + was_modified |= !insts_to_kill.empty(); for (Instruction* inst : insts_to_kill) context->KillInst(inst); insts_to_kill.clear(); @@ -165,6 +166,7 @@ void DecorationManager::RemoveDecorationsFrom( 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() && @@ -172,6 +174,7 @@ void DecorationManager::RemoveDecorationsFrom( decorations_info.decorate_insts.empty()) { id_to_decoration_insts_.erase(ids_iter); } + return was_modified; } std::vector<Instruction*> DecorationManager::GetDecorationsFor( diff --git a/source/opt/decoration_manager.h b/source/opt/decoration_manager.h index e1ae8d57..b753e6be 100644 --- a/source/opt/decoration_manager.h +++ b/source/opt/decoration_manager.h @@ -36,8 +36,9 @@ class DecorationManager { } DecorationManager() = delete; - // Changes all of the decorations (direct and through groups) where |pred| is - // true and that apply to |id| so that they no longer apply to |id|. + // 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 @@ -52,9 +53,9 @@ class DecorationManager { // removed, then the |OpGroupDecorate| and // |OpGroupMemberDecorate| for the group will be killed, but not the defining // |OpDecorationGroup| instruction. - void RemoveDecorationsFrom(uint32_t id, - std::function<bool(const Instruction&)> pred = - [](const Instruction&) { return true; }); + bool RemoveDecorationsFrom( + uint32_t id, std::function<bool(const Instruction&)> pred = + [](const Instruction&) { return true; }); // Removes all decorations from the result id of |inst|. // diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index b68549a0..5e950069 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp @@ -63,16 +63,7 @@ bool DescriptorScalarReplacement::IsCandidate(Instruction* var) { // All structures with descriptor assignments must be replaced by variables, // one for each of their members - with the exceptions of buffers. - // Buffers are represented as structures, but we shouldn't replace a buffer - // with its elements. All buffers have offset decorations for members of their - // structure types. - bool has_offset_decoration = false; - context()->get_decoration_mgr()->ForEachDecoration( - var_type_inst->result_id(), SpvDecorationOffset, - [&has_offset_decoration](const Instruction&) { - has_offset_decoration = true; - }); - if (has_offset_decoration) { + if (IsTypeOfStructuredBuffer(var_type_inst)) { return false; } @@ -99,6 +90,23 @@ bool DescriptorScalarReplacement::IsCandidate(Instruction* var) { return true; } +bool DescriptorScalarReplacement::IsTypeOfStructuredBuffer( + const Instruction* type) const { + if (type->opcode() != SpvOpTypeStruct) { + return false; + } + + // All buffers have offset decorations for members of their structure types. + // This is how we distinguish it from a structure of descriptors. + bool has_offset_decoration = false; + context()->get_decoration_mgr()->ForEachDecoration( + type->result_id(), SpvDecorationOffset, + [&has_offset_decoration](const Instruction&) { + has_offset_decoration = true; + }); + return has_offset_decoration; +} + bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) { std::vector<Instruction*> access_chain_work_list; std::vector<Instruction*> load_work_list; @@ -368,7 +376,8 @@ uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType( // The number of bindings consumed by a structure is the sum of the bindings // used by its members. - if (type_inst->opcode() == SpvOpTypeStruct) { + if (type_inst->opcode() == SpvOpTypeStruct && + !IsTypeOfStructuredBuffer(type_inst)) { uint32_t sum = 0; for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i)); diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h index c3aa0ea2..cd72fd30 100644 --- a/source/opt/desc_sroa.h +++ b/source/opt/desc_sroa.h @@ -93,6 +93,11 @@ class DescriptorScalarReplacement : public Pass { // bindings used by its members. uint32_t GetNumBindingsUsedByType(uint32_t type_id); + // 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(const Instruction* type) const; + // 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 the ith element of the diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp index 03da0d0d..04eb1326 100644 --- a/source/opt/fix_storage_class.cpp +++ b/source/opt/fix_storage_class.cpp @@ -222,6 +222,16 @@ bool FixStorageClass::PropagateType(Instruction* inst, uint32_t type_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); inst->SetInOperand(1, {copy_id}); context()->UpdateDefUse(inst); diff --git a/source/opt/generate_webgpu_initializers_pass.cpp b/source/opt/generate_webgpu_initializers_pass.cpp deleted file mode 100644 index eaed3c28..00000000 --- a/source/opt/generate_webgpu_initializers_pass.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// 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/generate_webgpu_initializers_pass.h" -#include "source/opt/ir_context.h" - -namespace spvtools { -namespace opt { - -using inst_iterator = InstructionList::iterator; - -namespace { - -bool NeedsWebGPUInitializer(Instruction* inst) { - if (inst->opcode() != SpvOpVariable) return false; - - auto storage_class = inst->GetSingleWordOperand(2); - if (storage_class != SpvStorageClassOutput && - storage_class != SpvStorageClassPrivate && - storage_class != SpvStorageClassFunction) { - return false; - } - - if (inst->NumOperands() > 3) return false; - - return true; -} - -} // namespace - -Pass::Status GenerateWebGPUInitializersPass::Process() { - auto* module = context()->module(); - bool changed = false; - - // Handle global/module scoped variables - for (auto iter = module->types_values_begin(); - iter != module->types_values_end(); ++iter) { - Instruction* inst = &(*iter); - - if (inst->opcode() == SpvOpConstantNull) { - null_constant_type_map_[inst->type_id()] = inst; - seen_null_constants_.insert(inst); - continue; - } - - if (!NeedsWebGPUInitializer(inst)) continue; - - changed = true; - - auto* constant_inst = GetNullConstantForVariable(inst); - if (!constant_inst) return Status::Failure; - - if (seen_null_constants_.find(constant_inst) == - seen_null_constants_.end()) { - constant_inst->InsertBefore(inst); - null_constant_type_map_[inst->type_id()] = inst; - seen_null_constants_.insert(inst); - } - AddNullInitializerToVariable(constant_inst, inst); - } - - // Handle local/function scoped variables - for (auto func = module->begin(); func != module->end(); ++func) { - auto block = func->entry().get(); - for (auto iter = block->begin(); - iter != block->end() && iter->opcode() == SpvOpVariable; ++iter) { - Instruction* inst = &(*iter); - if (!NeedsWebGPUInitializer(inst)) continue; - - changed = true; - auto* constant_inst = GetNullConstantForVariable(inst); - if (!constant_inst) return Status::Failure; - - AddNullInitializerToVariable(constant_inst, inst); - } - } - - return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; -} - -Instruction* GenerateWebGPUInitializersPass::GetNullConstantForVariable( - Instruction* variable_inst) { - auto constant_mgr = context()->get_constant_mgr(); - auto* def_use_mgr = get_def_use_mgr(); - - auto* ptr_inst = def_use_mgr->GetDef(variable_inst->type_id()); - auto type_id = ptr_inst->GetInOperand(1).words[0]; - if (null_constant_type_map_.find(type_id) == null_constant_type_map_.end()) { - auto* constant_type = context()->get_type_mgr()->GetType(type_id); - auto* constant = constant_mgr->GetConstant(constant_type, {}); - return constant_mgr->GetDefiningInstruction(constant, type_id); - } else { - return null_constant_type_map_[type_id]; - } -} - -void GenerateWebGPUInitializersPass::AddNullInitializerToVariable( - Instruction* constant_inst, Instruction* variable_inst) { - auto constant_id = constant_inst->result_id(); - variable_inst->AddOperand(Operand(SPV_OPERAND_TYPE_ID, {constant_id})); - get_def_use_mgr()->AnalyzeInstUse(variable_inst); -} - -} // namespace opt -} // namespace spvtools diff --git a/source/opt/generate_webgpu_initializers_pass.h b/source/opt/generate_webgpu_initializers_pass.h deleted file mode 100644 index f95e84c5..00000000 --- a/source/opt/generate_webgpu_initializers_pass.h +++ /dev/null @@ -1,62 +0,0 @@ -// 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_GENERATE_WEBGPU_INITIALIZERS_PASS_H_ -#define SOURCE_OPT_GENERATE_WEBGPU_INITIALIZERS_PASS_H_ - -#include "source/opt/ir_context.h" -#include "source/opt/module.h" -#include "source/opt/pass.h" - -namespace spvtools { -namespace opt { - -// Adds initializers to variables with storage classes Output, Private, and -// Function if they are missing. In the WebGPU environment these storage classes -// require that the variables are initialized. Currently they are initialized to -// NULL, though in the future some of them may be initialized to the first value -// that is stored in them, if that was a constant. -class GenerateWebGPUInitializersPass : public Pass { - public: - const char* name() const override { return "generate-webgpu-initializers"; } - 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; - } - - private: - using NullConstantTypeMap = std::unordered_map<uint32_t, Instruction*>; - NullConstantTypeMap null_constant_type_map_; - std::unordered_set<Instruction*> seen_null_constants_; - - Instruction* GetNullConstantForVariable(Instruction* variable_inst); - void AddNullInitializerToVariable(Instruction* constant_inst, - Instruction* variable_inst); -}; - -} // namespace opt -} // namespace spvtools - -#endif // SOURCE_OPT_GENERATE_WEBGPU_INITIALIZERS_PASS_H_ diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp index 88f395f0..8159ebf7 100644 --- a/source/opt/inline_pass.cpp +++ b/source/opt/inline_pass.cpp @@ -383,9 +383,7 @@ std::unique_ptr<BasicBlock> InlinePass::InlineReturn( uint32_t returnLabelId = 0; for (auto callee_block_itr = calleeFn->begin(); callee_block_itr != calleeFn->end(); ++callee_block_itr) { - if (callee_block_itr->tail()->opcode() == SpvOpUnreachable || - callee_block_itr->tail()->opcode() == SpvOpKill || - callee_block_itr->tail()->opcode() == SpvOpTerminateInvocation) { + if (spvOpcodeIsAbort(callee_block_itr->tail()->opcode())) { returnLabelId = context()->TakeNextId(); break; } @@ -759,8 +757,7 @@ bool InlinePass::IsInlinableFunction(Function* func) { bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const { return !func->WhileEachInst([](Instruction* inst) { - const auto opcode = inst->opcode(); - return (opcode != SpvOpKill) && (opcode != SpvOpTerminateInvocation); + return !spvOpcodeTerminatesExecution(inst->opcode()); }); } diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp index 00857343..5607239a 100644 --- a/source/opt/inst_bindless_check_pass.cpp +++ b/source/opt/inst_bindless_check_pass.cpp @@ -27,13 +27,16 @@ static const int kSpvCopyObjectOperandIdInIdx = 0; static const int kSpvLoadPtrIdInIdx = 0; static const int kSpvAccessChainBaseIdInIdx = 0; static const int kSpvAccessChainIndex0IdInIdx = 1; +static const int kSpvTypeArrayTypeIdInIdx = 0; static const int kSpvTypeArrayLengthIdInIdx = 1; static const int kSpvConstantValueInIdx = 0; static const int kSpvVariableStorageClassInIdx = 0; +static const int kSpvTypePtrTypeIdInIdx = 1; static const int kSpvTypeImageDim = 1; static const int kSpvTypeImageDepth = 2; static const int kSpvTypeImageArrayed = 3; static const int kSpvTypeImageMS = 4; +static const int kSpvTypeImageSampled = 5; } // anonymous namespace // Avoid unused variable warning/error on Linux @@ -206,13 +209,40 @@ bool InstBindlessCheckPass::AnalyzeDescriptorReference(Instruction* ref_inst, var_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx); switch (storage_class) { case SpvStorageClassUniform: - case SpvStorageClassUniformConstant: case SpvStorageClassStorageBuffer: break; default: return false; break; } + // Check for deprecated storage block form + if (storage_class == SpvStorageClassUniform) { + uint32_t var_ty_id = var_inst->type_id(); + Instruction* var_ty_inst = get_def_use_mgr()->GetDef(var_ty_id); + uint32_t ptr_ty_id = + var_ty_inst->GetSingleWordInOperand(kSpvTypePtrTypeIdInIdx); + Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id); + SpvOp ptr_ty_op = ptr_ty_inst->opcode(); + uint32_t block_ty_id = + (ptr_ty_op == SpvOpTypeArray || ptr_ty_op == SpvOpTypeRuntimeArray) + ? ptr_ty_inst->GetSingleWordInOperand(kSpvTypeArrayTypeIdInIdx) + : ptr_ty_id; + assert(get_def_use_mgr()->GetDef(block_ty_id)->opcode() == + SpvOpTypeStruct && + "unexpected block type"); + bool block_found = get_decoration_mgr()->FindDecoration( + block_ty_id, SpvDecorationBlock, + [](const Instruction&) { return true; }); + if (!block_found) { + // If block decoration not found, verify deprecated form of SSBO + bool buffer_block_found = get_decoration_mgr()->FindDecoration( + block_ty_id, SpvDecorationBufferBlock, + [](const Instruction&) { return true; }); + USE_ASSERT(buffer_block_found && "block decoration not found"); + storage_class = SpvStorageClassStorageBuffer; + } + } + ref->strg_class = storage_class; Instruction* desc_type_inst = GetPointeeTypeInst(var_inst); switch (desc_type_inst->opcode()) { case SpvOpTypeArray: @@ -665,8 +695,10 @@ void InstBindlessCheckPass::GenDescInitCheckCode( // for the referenced value. Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, ref_id, init_id); - uint32_t error = - init_check ? kInstErrorBindlessUninit : kInstErrorBindlessBuffOOB; + uint32_t error = init_check ? kInstErrorBindlessUninit + : (ref.strg_class == SpvStorageClassUniform + ? kInstErrorBuffOOBUniform + : kInstErrorBuffOOBStorage); uint32_t error_id = builder.GetUintConstantId(error); GenCheckCode(ult_inst->result_id(), error_id, init_check ? 0 : ref_id, init_check ? builder.GetUintConstantId(0u) : init_id, stage_idx, @@ -732,7 +764,11 @@ void InstBindlessCheckPass::GenTexBuffCheckCode( // for the referenced value. Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, coord_id, size_id); - uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBuffOOB); + uint32_t error = + (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageSampled) == 2) + ? kInstErrorBuffOOBStorageTexel + : kInstErrorBuffOOBUniformTexel; + uint32_t error_id = builder.GetUintConstantId(error); GenCheckCode(ult_inst->result_id(), error_id, coord_id, size_id, stage_idx, &ref, new_blocks); // Move original block's remaining code into remainder/merge block and add diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h index a7dff75f..cd961805 100644 --- a/source/opt/inst_bindless_check_pass.h +++ b/source/opt/inst_bindless_check_pass.h @@ -130,6 +130,7 @@ class InstBindlessCheckPass : public InstrumentPass { uint32_t ptr_id; uint32_t var_id; uint32_t desc_idx_id; + uint32_t strg_class; Instruction* ref_inst; } RefAnalysis; diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 252e8cb5..3e557dd7 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -97,10 +97,14 @@ struct Operand { assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER); assert(1 <= words.size()); assert(words.size() <= 2); - // Load the low word. - uint64_t result = uint64_t(words[0]); + 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) { - result = result | (uint64_t(words[1]) << 32); + uint32_t high = words[1]; + result = result | (uint64_t(high) << 32); } return result; } @@ -205,7 +209,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> { Instruction(Instruction&&); Instruction& operator=(Instruction&&); - virtual ~Instruction() = default; + ~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. diff --git a/source/opt/instruction_list.h b/source/opt/instruction_list.h index 417cbd76..b3e42745 100644 --- a/source/opt/instruction_list.h +++ b/source/opt/instruction_list.h @@ -53,7 +53,7 @@ class InstructionList : public utils::IntrusiveList<Instruction> { } // Destroy this list and any instructions in the list. - inline virtual ~InstructionList(); + inline ~InstructionList() override; class iterator : public utils::IntrusiveList<Instruction>::iterator { public: diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index 06099ce0..e443ebb5 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp @@ -41,6 +41,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { ++inst_index_; const auto opcode = static_cast<SpvOp>(inst->opcode); if (IsDebugLineInst(opcode)) { + module()->SetContainsDebugInfo(); last_line_inst_.reset(); dbg_line_info_.push_back( Instruction(module()->context(), *inst, last_dbg_scope_)); @@ -61,12 +62,12 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { inlined_at = inst->words[kInlinedAtIndex]; last_dbg_scope_ = DebugScope(inst->words[kLexicalScopeIndex], inlined_at); - module()->SetContainsDebugScope(); + module()->SetContainsDebugInfo(); return true; } if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) { last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); - module()->SetContainsDebugScope(); + module()->SetContainsDebugInfo(); return true; } } else { @@ -78,12 +79,12 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { inlined_at = inst->words[kInlinedAtIndex]; last_dbg_scope_ = DebugScope(inst->words[kLexicalScopeIndex], inlined_at); - module()->SetContainsDebugScope(); + module()->SetContainsDebugInfo(); return true; } if (ext_inst_key == DebugInfoDebugNoScope) { last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); - module()->SetContainsDebugScope(); + module()->SetContainsDebugInfo(); return true; } } @@ -137,7 +138,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { return false; } block_ = MakeUnique<BasicBlock>(std::move(spv_inst)); - } else if (IsTerminatorInst(opcode)) { + } else if (spvOpcodeIsBlockTerminator(opcode)) { if (function_ == nullptr) { Error(consumer_, src, loc, "terminator instruction outside function"); return false; diff --git a/source/opt/legalize_vector_shuffle_pass.cpp b/source/opt/legalize_vector_shuffle_pass.cpp deleted file mode 100644 index b5d5d599..00000000 --- a/source/opt/legalize_vector_shuffle_pass.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// 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/legalize_vector_shuffle_pass.h" - -#include "source/opt/ir_context.h" - -namespace spvtools { -namespace opt { - -Pass::Status LegalizeVectorShufflePass::Process() { - bool changed = false; - context()->module()->ForEachInst([&changed](Instruction* inst) { - if (inst->opcode() != SpvOpVectorShuffle) return; - - for (uint32_t idx = 2; idx < inst->NumInOperands(); ++idx) { - auto literal = inst->GetSingleWordInOperand(idx); - if (literal != 0xFFFFFFFF) continue; - changed = true; - inst->SetInOperand(idx, {0}); - } - }); - - return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; -} - -} // namespace opt -} // namespace spvtools diff --git a/source/opt/legalize_vector_shuffle_pass.h b/source/opt/legalize_vector_shuffle_pass.h deleted file mode 100644 index ca6e1dfb..00000000 --- a/source/opt/legalize_vector_shuffle_pass.h +++ /dev/null @@ -1,53 +0,0 @@ -// 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_LEGALIZE_VECTOR_SHUFFLE_PASS_H_ -#define SOURCE_OPT_LEGALIZE_VECTOR_SHUFFLE_PASS_H_ - -#include "source/opt/ir_context.h" -#include "source/opt/module.h" -#include "source/opt/pass.h" - -namespace spvtools { -namespace opt { - -// Converts any usages of 0xFFFFFFFF for the literals in OpVectorShuffle to a -// literal 0. This is needed because using OxFFFFFFFF is forbidden by the WebGPU -// spec. 0xFFFFFFFF in the main spec indicates that the result for this -// component has no source, thus is undefined. Since this is undefined -// behaviour we are free to use 0. -class LegalizeVectorShufflePass : public Pass { - public: - const char* name() const override { return "legalize-vector-shuffle"; } - 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_LEGALIZE_VECTOR_SHUFFLE_PASS_H_ diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index b8d9091a..d322a2fb 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -175,7 +175,7 @@ bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst, for (auto* decl : invisible_decls) { if (dominator_analysis->Dominates(store_inst, decl)) { context()->get_debug_info_mgr()->AddDebugValueForDecl(decl, value_id, - decl); + decl, store_inst); modified = true; } } diff --git a/source/opt/module.cpp b/source/opt/module.cpp index 9d3b0edc..0c886010 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -188,7 +188,7 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const { i->ToBinaryWithoutAttachedDebugInsts(binary); } // Update the last line instruction. - if (IsTerminatorInst(opcode) || opcode == SpvOpNoLine) { + if (spvOpcodeIsBlockTerminator(opcode) || opcode == SpvOpNoLine) { last_line_inst = nullptr; } else if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { between_merge_and_branch = true; diff --git a/source/opt/module.h b/source/opt/module.h index 2c96f029..d609b603 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -49,7 +49,7 @@ class Module { using const_inst_iterator = InstructionList::const_iterator; // Creates an empty module with zero'd header. - Module() : header_({}), contains_debug_scope_(false) {} + Module() : header_({}), contains_debug_info_(false) {} // Sets the header to the given |header|. void SetHeader(const ModuleHeader& header) { header_ = header; } @@ -119,9 +119,9 @@ class Module { // Appends a function to this module. inline void AddFunction(std::unique_ptr<Function> f); - // Sets |contains_debug_scope_| as true. - inline void SetContainsDebugScope(); - inline bool ContainsDebugScope() { return contains_debug_scope_; } + // 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. @@ -301,8 +301,8 @@ class Module { // any instruction. We record them here, so they will not be lost. std::vector<Instruction> trailing_dbg_line_info_; - // This module contains DebugScope or DebugNoScope. - bool contains_debug_scope_; + // This module contains DebugScope/DebugNoScope or OpLine/OpNoLine. + bool contains_debug_info_; }; // Pretty-prints |module| to |str|. Returns |str|. @@ -364,7 +364,7 @@ inline void Module::AddFunction(std::unique_ptr<Function> f) { functions_.emplace_back(std::move(f)); } -inline void Module::SetContainsDebugScope() { contains_debug_scope_ = true; } +inline void Module::SetContainsDebugInfo() { contains_debug_info_ = true; } inline Module::inst_iterator Module::capability_begin() { return capabilities_.begin(); diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 8726ff93..909442cc 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -62,7 +62,9 @@ struct Optimizer::Impl { opt::PassManager pass_manager; // Internal implementation pass manager. }; -Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {} +Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) { + assert(env != SPV_ENV_WEBGPU_0); +} Optimizer::~Optimizer() {} @@ -239,23 +241,6 @@ Optimizer& Optimizer::RegisterSizePasses() { .RegisterPass(CreateCFGCleanupPass()); } -Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() { - return RegisterPass(CreateStripAtomicCounterMemoryPass()) - .RegisterPass(CreateGenerateWebGPUInitializersPass()) - .RegisterPass(CreateLegalizeVectorShufflePass()) - .RegisterPass(CreateSplitInvalidUnreachablePass()) - .RegisterPass(CreateEliminateDeadConstantPass()) - .RegisterPass(CreateFlattenDecorationPass()) - .RegisterPass(CreateAggressiveDCEPass()) - .RegisterPass(CreateDeadBranchElimPass()) - .RegisterPass(CreateCompactIdsPass()); -} - -Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() { - return RegisterPass(CreateDecomposeInitializedVariablesPass()) - .RegisterPass(CreateCompactIdsPass()); -} - bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) { for (const auto& flag : flags) { if (!RegisterPassFromFlag(flag)) { @@ -298,9 +283,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { // // 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-atomic-counter-memory") { - RegisterPass(CreateStripAtomicCounterMemoryPass()); - } else if (pass_name == "strip-debug") { + if (pass_name == "strip-debug") { RegisterPass(CreateStripDebugInfoPass()); } else if (pass_name == "strip-reflect") { RegisterPass(CreateStripReflectInfoPass()); @@ -505,14 +488,6 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterSizePasses(); } else if (pass_name == "legalize-hlsl") { RegisterLegalizationPasses(); - } else if (pass_name == "generate-webgpu-initializers") { - RegisterPass(CreateGenerateWebGPUInitializersPass()); - } else if (pass_name == "legalize-vector-shuffle") { - RegisterPass(CreateLegalizeVectorShufflePass()); - } else if (pass_name == "split-invalid-unreachable") { - RegisterPass(CreateSplitInvalidUnreachablePass()); - } else if (pass_name == "decompose-initialized-variables") { - RegisterPass(CreateDecomposeInitializedVariablesPass()); } else if (pass_name == "graphics-robust-access") { RegisterPass(CreateGraphicsRobustAccessPass()); } else if (pass_name == "wrap-opkill") { @@ -583,10 +558,12 @@ bool Optimizer::Run(const uint32_t* original_binary, #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 instructions. + // contains DebugScope or OpLine/OpNoLine instructions. if (status == opt::Pass::Status::SuccessWithoutChange && - !context->module()->ContainsDebugScope()) { + !context->module()->ContainsDebugInfo()) { std::vector<uint32_t> optimized_binary_with_nop; context->module()->ToBinary(&optimized_binary_with_nop, /* skip_nop = */ false); @@ -627,11 +604,6 @@ Optimizer::PassToken CreateNullPass() { return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>()); } -Optimizer::PassToken CreateStripAtomicCounterMemoryPass() { - return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::StripAtomicCounterMemoryPass>()); -} - Optimizer::PassToken CreateStripDebugInfoPass() { return MakeUnique<Optimizer::PassToken::Impl>( MakeUnique<opt::StripDebugInfoPass>()); @@ -929,31 +901,11 @@ Optimizer::PassToken CreateCodeSinkingPass() { MakeUnique<opt::CodeSinkingPass>()); } -Optimizer::PassToken CreateGenerateWebGPUInitializersPass() { - return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::GenerateWebGPUInitializersPass>()); -} - Optimizer::PassToken CreateFixStorageClassPass() { return MakeUnique<Optimizer::PassToken::Impl>( MakeUnique<opt::FixStorageClass>()); } -Optimizer::PassToken CreateLegalizeVectorShufflePass() { - return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::LegalizeVectorShufflePass>()); -} - -Optimizer::PassToken CreateDecomposeInitializedVariablesPass() { - return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::DecomposeInitializedVariablesPass>()); -} - -Optimizer::PassToken CreateSplitInvalidUnreachablePass() { - return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::SplitInvalidUnreachablePass>()); -} - Optimizer::PassToken CreateGraphicsRobustAccessPass() { return MakeUnique<Optimizer::PassToken::Impl>( MakeUnique<opt::GraphicsRobustAccessPass>()); diff --git a/source/opt/passes.h b/source/opt/passes.h index d47cc1ce..1bc94c7e 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -30,7 +30,6 @@ #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/decompose_initialized_variables_pass.h" #include "source/opt/desc_sroa.h" #include "source/opt/eliminate_dead_constant_pass.h" #include "source/opt/eliminate_dead_functions_pass.h" @@ -40,7 +39,6 @@ #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/generate_webgpu_initializers_pass.h" #include "source/opt/graphics_robust_access_pass.h" #include "source/opt/if_conversion.h" #include "source/opt/inline_exhaustive_pass.h" @@ -48,7 +46,6 @@ #include "source/opt/inst_bindless_check_pass.h" #include "source/opt/inst_buff_addr_check_pass.h" #include "source/opt/inst_debug_printf_pass.h" -#include "source/opt/legalize_vector_shuffle_pass.h" #include "source/opt/licm_pass.h" #include "source/opt/local_access_chain_convert_pass.h" #include "source/opt/local_redundancy_elimination.h" @@ -70,10 +67,8 @@ #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_invalid_unreachable_pass.h" #include "source/opt/ssa_rewrite_pass.h" #include "source/opt/strength_reduction_pass.h" -#include "source/opt/strip_atomic_counter_memory_pass.h" #include "source/opt/strip_debug_info_pass.h" #include "source/opt/strip_reflect_info_pass.h" #include "source/opt/unify_const_pass.h" diff --git a/source/opt/reflect.h b/source/opt/reflect.h index d374e682..c7d46df5 100644 --- a/source/opt/reflect.h +++ b/source/opt/reflect.h @@ -59,10 +59,6 @@ inline bool IsCompileTimeConstantInst(SpvOp opcode) { inline bool IsSpecConstantInst(SpvOp opcode) { return opcode >= SpvOpSpecConstantTrue && opcode <= SpvOpSpecConstantOp; } -inline bool IsTerminatorInst(SpvOp opcode) { - return (opcode >= SpvOpBranch && opcode <= SpvOpUnreachable) || - (opcode == SpvOpTerminateInvocation); -} } // namespace opt } // namespace spvtools diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index c8e0da5b..ba2d0675 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp @@ -175,7 +175,8 @@ bool ScalarReplacementPass::ReplaceWholeDebugDeclare( Instruction* added_dbg_value = context()->get_debug_info_mgr()->AddDebugValueForDecl( dbg_decl, /*value_id=*/var->result_id(), - /*insert_before=*/var->NextNode()); + /*insert_before=*/var->NextNode(), /*scope_and_line=*/dbg_decl); + if (added_dbg_value == nullptr) return false; added_dbg_value->AddOperand( {SPV_OPERAND_TYPE_ID, diff --git a/source/opt/split_invalid_unreachable_pass.cpp b/source/opt/split_invalid_unreachable_pass.cpp deleted file mode 100644 index 31cfbc33..00000000 --- a/source/opt/split_invalid_unreachable_pass.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// 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/split_invalid_unreachable_pass.h" - -#include "source/opt/ir_builder.h" -#include "source/opt/ir_context.h" - -namespace spvtools { -namespace opt { - -Pass::Status SplitInvalidUnreachablePass::Process() { - bool changed = false; - std::unordered_set<uint32_t> entry_points; - for (auto entry_point : context()->module()->entry_points()) { - entry_points.insert(entry_point.GetSingleWordOperand(1)); - } - - for (auto func = context()->module()->begin(); - func != context()->module()->end(); ++func) { - if (entry_points.find(func->result_id()) == entry_points.end()) continue; - std::unordered_set<uint32_t> continue_targets; - std::unordered_set<uint32_t> merge_blocks; - std::unordered_set<BasicBlock*> unreachable_blocks; - for (auto block = func->begin(); block != func->end(); ++block) { - unreachable_blocks.insert(&*block); - uint32_t continue_target = block->ContinueBlockIdIfAny(); - if (continue_target != 0) continue_targets.insert(continue_target); - uint32_t merge_block = block->MergeBlockIdIfAny(); - if (merge_block != 0) merge_blocks.insert(merge_block); - } - - cfg()->ForEachBlockInPostOrder( - func->entry().get(), [&unreachable_blocks](BasicBlock* inner_block) { - unreachable_blocks.erase(inner_block); - }); - - for (auto unreachable : unreachable_blocks) { - uint32_t block_id = unreachable->id(); - if (continue_targets.find(block_id) == continue_targets.end() || - merge_blocks.find(block_id) == merge_blocks.end()) { - continue; - } - - std::vector<std::tuple<Instruction*, uint32_t>> usages; - context()->get_def_use_mgr()->ForEachUse( - unreachable->GetLabelInst(), - [&usages](Instruction* use, uint32_t idx) { - if ((use->opcode() == SpvOpLoopMerge && idx == 0) || - use->opcode() == SpvOpSelectionMerge) { - usages.push_back(std::make_pair(use, idx)); - } - }); - - for (auto usage : usages) { - Instruction* use; - uint32_t idx; - std::tie(use, idx) = usage; - uint32_t new_id = context()->TakeNextId(); - std::unique_ptr<Instruction> new_label( - new Instruction(context(), SpvOpLabel, 0, new_id, {})); - get_def_use_mgr()->AnalyzeInstDefUse(new_label.get()); - std::unique_ptr<BasicBlock> new_block( - new BasicBlock(std::move(new_label))); - auto* block_ptr = new_block.get(); - InstructionBuilder builder(context(), new_block.get(), - IRContext::kAnalysisDefUse | - IRContext::kAnalysisInstrToBlockMapping); - builder.AddUnreachable(); - cfg()->RegisterBlock(block_ptr); - (&*func)->InsertBasicBlockBefore(std::move(new_block), unreachable); - use->SetInOperand(0, {new_id}); - get_def_use_mgr()->UpdateDefUse(use); - cfg()->AddEdges(block_ptr); - changed = true; - } - } - } - - return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; -} - -} // namespace opt -} // namespace spvtools diff --git a/source/opt/split_invalid_unreachable_pass.h b/source/opt/split_invalid_unreachable_pass.h deleted file mode 100644 index a5613448..00000000 --- a/source/opt/split_invalid_unreachable_pass.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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_SPLIT_INVALID_UNREACHABLE_PASS_H_ -#define SOURCE_OPT_SPLIT_INVALID_UNREACHABLE_PASS_H_ - -#include "source/opt/ir_context.h" -#include "source/opt/module.h" -#include "source/opt/pass.h" - -namespace spvtools { -namespace opt { - -// Attempts to legalize for WebGPU by splitting up invalid unreachable blocks. -// Specifically, looking for cases of unreachable merge-blocks and -// continue-targets that are used more then once, which is illegal in WebGPU. -class SplitInvalidUnreachablePass : public Pass { - public: - const char* name() const override { return "split-invalid-unreachable"; } - 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_SPLIT_INVALID_UNREACHABLE_PASS_H_ diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp index 0489f03a..81770d77 100644 --- a/source/opt/ssa_rewrite_pass.cpp +++ b/source/opt/ssa_rewrite_pass.cpp @@ -658,8 +658,8 @@ Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) { // a = 3; // foo(3); // After inlining: - // a = 3; // we want to specify "DebugValue: %x = %int_3" - // foo and x disappeared! + // a = 3; + // foo and x disappeared but we want to specify "DebugValue: %x = %int_3". // // We want to specify the value for the variable using |defs_at_block_[bb]|, // where |bb| is the basic block contains the decl. @@ -681,16 +681,17 @@ Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) { if (value && (pass_->context()->get_instr_block(value) == nullptr || dom_tree->Dominates(value, decl))) { if (pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl( - decl, value->result_id(), decl) == nullptr) { + decl, value->result_id(), decl, value) == nullptr) { return Pass::Status::Failure; } } else { // If |value| in the same basic block does not dominate |decl|, we can // assign the value in the immediate dominator. value_id = GetValueAtBlock(var_id, dom_tree->ImmediateDominator(bb)); + if (value_id) value = pass_->get_def_use_mgr()->GetDef(value_id); if (value_id && pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl( - decl, value_id, decl) == nullptr) { + decl, value_id, decl, value) == nullptr) { return Pass::Status::Failure; } } diff --git a/source/opt/strip_atomic_counter_memory_pass.cpp b/source/opt/strip_atomic_counter_memory_pass.cpp deleted file mode 100644 index 47714b74..00000000 --- a/source/opt/strip_atomic_counter_memory_pass.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// 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/strip_atomic_counter_memory_pass.h" -#include "source/opt/ir_context.h" - -namespace spvtools { -namespace opt { - -Pass::Status StripAtomicCounterMemoryPass::Process() { - bool changed = false; - context()->module()->ForEachInst([this, &changed](Instruction* inst) { - auto indices = spvOpcodeMemorySemanticsOperandIndices(inst->opcode()); - if (indices.empty()) return; - - for (auto idx : indices) { - auto mem_sem_id = inst->GetSingleWordOperand(idx); - const auto& mem_sem_inst = - context()->get_def_use_mgr()->GetDef(mem_sem_id); - // The spec explicitly says that this id must be an OpConstant - auto mem_sem_val = mem_sem_inst->GetSingleWordOperand(2); - if (!(mem_sem_val & SpvMemorySemanticsAtomicCounterMemoryMask)) { - continue; - } - mem_sem_val &= ~SpvMemorySemanticsAtomicCounterMemoryMask; - - analysis::Integer int_type(32, false); - const analysis::Type* uint32_type = - context()->get_type_mgr()->GetRegisteredType(&int_type); - auto* new_const = context()->get_constant_mgr()->GetConstant( - uint32_type, {mem_sem_val}); - auto* new_const_inst = - context()->get_constant_mgr()->GetDefiningInstruction(new_const); - auto new_const_id = new_const_inst->result_id(); - - inst->SetOperand(idx, {new_const_id}); - context()->UpdateDefUse(inst); - changed = true; - } - }); - - return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; -} - -} // namespace opt -} // namespace spvtools diff --git a/source/opt/strip_atomic_counter_memory_pass.h b/source/opt/strip_atomic_counter_memory_pass.h deleted file mode 100644 index 62e274a1..00000000 --- a/source/opt/strip_atomic_counter_memory_pass.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_ -#define SOURCE_OPT_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_ - -#include "source/opt/ir_context.h" -#include "source/opt/module.h" -#include "source/opt/pass.h" - -namespace spvtools { -namespace opt { - -// Removes the AtomicCounterMemory bit from the value being passed into memory -// semantics. This bit being set is ignored in Vulkan environments and -// forbidden WebGPU ones. -class StripAtomicCounterMemoryPass : public Pass { - public: - const char* name() const override { return "strip-atomic-counter-memory"; } - 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_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_ diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index e2ff99cb..f20ebb4f 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp @@ -14,6 +14,7 @@ #include "source/spirv_target_env.h" +#include <cassert> #include <cstring> #include <string> @@ -61,7 +62,8 @@ const char* spvTargetEnvDescription(spv_target_env env) { case SPV_ENV_VULKAN_1_1: return "SPIR-V 1.3 (under Vulkan 1.1 semantics)"; case SPV_ENV_WEBGPU_0: - return "SPIR-V 1.3 (under WIP WebGPU semantics)"; + assert(false); + break; case SPV_ENV_UNIVERSAL_1_4: return "SPIR-V 1.4"; case SPV_ENV_VULKAN_1_1_SPIRV_1_4: @@ -98,8 +100,10 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { return SPV_SPIRV_VERSION_WORD(1, 2); case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_VULKAN_1_1: - case SPV_ENV_WEBGPU_0: return SPV_SPIRV_VERSION_WORD(1, 3); + case SPV_ENV_WEBGPU_0: + assert(false); + break; case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: return SPV_SPIRV_VERSION_WORD(1, 4); @@ -134,7 +138,6 @@ static const std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = { {"opengl4.2", SPV_ENV_OPENGL_4_2}, {"opengl4.3", SPV_ENV_OPENGL_4_3}, {"opengl4.5", SPV_ENV_OPENGL_4_5}, - {"webgpu0", SPV_ENV_WEBGPU_0}, }; bool spvParseTargetEnv(const char* s, spv_target_env* env) { @@ -200,7 +203,6 @@ bool spvIsVulkanEnv(spv_target_env env) { case SPV_ENV_OPENCL_2_2: case SPV_ENV_OPENCL_EMBEDDED_2_2: case SPV_ENV_UNIVERSAL_1_3: - case SPV_ENV_WEBGPU_0: case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: return false; @@ -209,6 +211,9 @@ bool spvIsVulkanEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_VULKAN_1_2: return true; + case SPV_ENV_WEBGPU_0: + assert(false); + break; } return false; } @@ -226,7 +231,6 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_2: case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_VULKAN_1_1: - case SPV_ENV_WEBGPU_0: case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: @@ -241,38 +245,9 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_OPENCL_2_1: case SPV_ENV_OPENCL_2_2: return true; - } - return false; -} - -bool spvIsWebGPUEnv(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_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: - return false; case SPV_ENV_WEBGPU_0: - return true; + assert(false); + break; } return false; } @@ -293,7 +268,6 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_OPENCL_EMBEDDED_2_2: case SPV_ENV_OPENCL_2_1: case SPV_ENV_OPENCL_2_2: - case SPV_ENV_WEBGPU_0: case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: @@ -305,14 +279,13 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_OPENGL_4_3: case SPV_ENV_OPENGL_4_5: return true; + case SPV_ENV_WEBGPU_0: + assert(false); + break; } return false; } -bool spvIsVulkanOrWebGPUEnv(spv_target_env env) { - return spvIsVulkanEnv(env) || spvIsWebGPUEnv(env); -} - std::string spvLogStringForEnv(spv_target_env env) { switch (env) { case SPV_ENV_OPENCL_1_2: @@ -338,9 +311,6 @@ std::string spvLogStringForEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_2: return "Vulkan"; } - case SPV_ENV_WEBGPU_0: { - return "WebGPU"; - } case SPV_ENV_UNIVERSAL_1_0: case SPV_ENV_UNIVERSAL_1_1: case SPV_ENV_UNIVERSAL_1_2: @@ -349,6 +319,9 @@ std::string spvLogStringForEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: { return "Universal"; } + case SPV_ENV_WEBGPU_0: + assert(false); + break; } return "Unknown"; } diff --git a/source/spirv_target_env.h b/source/spirv_target_env.h index 1bdedf91..a804d615 100644 --- a/source/spirv_target_env.h +++ b/source/spirv_target_env.h @@ -25,20 +25,14 @@ 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 WEBGPU environment, false otherwise. -bool spvIsWebGPUEnv(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 a VULKAN or WEBGPU environment, false otherwise. -bool spvIsVulkanOrWebGPUEnv(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", "WebGPU", "OpenCL", etc. +// 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 diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp index 01aa7974..2716cca9 100644 --- a/source/spirv_validator_options.cpp +++ b/source/spirv_validator_options.cpp @@ -111,6 +111,11 @@ void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options, 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; diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h index b7da5d8e..baaa5359 100644 --- a/source/spirv_validator_options.h +++ b/source/spirv_validator_options.h @@ -45,6 +45,7 @@ struct spv_validator_options_t { relax_block_layout(false), uniform_buffer_standard_layout(false), scalar_block_layout(false), + workgroup_scalar_block_layout(false), skip_block_layout(false), before_hlsl_legalization(false) {} @@ -54,6 +55,7 @@ struct spv_validator_options_t { bool relax_block_layout; bool uniform_buffer_standard_layout; bool scalar_block_layout; + bool workgroup_scalar_block_layout; bool skip_block_layout; bool before_hlsl_legalization; }; diff --git a/source/table.cpp b/source/table.cpp index 8340e8e2..d4a2d7e9 100644 --- a/source/table.cpp +++ b/source/table.cpp @@ -38,7 +38,6 @@ spv_context spvContextCreate(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_VULKAN_1_1: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: - case SPV_ENV_WEBGPU_0: case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: diff --git a/source/val/validate.cpp b/source/val/validate.cpp index d6e992b1..a2e116b1 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -111,57 +111,6 @@ spv_result_t ValidateForwardDecls(ValidationState_t& _) { << id_str.substr(0, id_str.size() - 1); } -std::vector<std::string> CalculateNamesForEntryPoint(ValidationState_t& _, - const uint32_t id) { - auto id_descriptions = _.entry_point_descriptions(id); - auto id_names = std::vector<std::string>(); - id_names.reserve((id_descriptions.size())); - - for (auto description : id_descriptions) id_names.push_back(description.name); - - return id_names; -} - -spv_result_t ValidateEntryPointNameUnique(ValidationState_t& _, - const uint32_t id) { - auto id_names = CalculateNamesForEntryPoint(_, id); - const auto names = - std::unordered_set<std::string>(id_names.begin(), id_names.end()); - - if (id_names.size() != names.size()) { - std::sort(id_names.begin(), id_names.end()); - for (size_t i = 0; i < id_names.size() - 1; i++) { - if (id_names[i] == id_names[i + 1]) { - return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id)) - << "Entry point name \"" << id_names[i] - << "\" is not unique, which is not allow in WebGPU env."; - } - } - } - - for (const auto other_id : _.entry_points()) { - if (other_id == id) continue; - const auto other_id_names = CalculateNamesForEntryPoint(_, other_id); - for (const auto& other_id_name : other_id_names) { - if (names.find(other_id_name) != names.end()) { - return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id)) - << "Entry point name \"" << other_id_name - << "\" is not unique, which is not allow in WebGPU env."; - } - } - } - - return SPV_SUCCESS; -} - -spv_result_t ValidateEntryPointNamesUnique(ValidationState_t& _) { - for (const auto id : _.entry_points()) { - auto result = ValidateEntryPointNameUnique(_, id); - if (result != SPV_SUCCESS) return result; - } - return SPV_SUCCESS; -} - // 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 @@ -169,8 +118,7 @@ spv_result_t ValidateEntryPointNamesUnique(ValidationState_t& _) { // * No function can be targeted by both an OpEntryPoint instruction and an // OpFunctionCall instruction. // -// Additionally enforces that entry points for Vulkan and WebGPU should not have -// recursion. And that entry names should be unique for WebGPU. +// Additionally enforces that entry points for Vulkan should not have recursion. spv_result_t ValidateEntryPoints(ValidationState_t& _) { _.ComputeFunctionToEntryPointMapping(); _.ComputeRecursiveEntryPoints(); @@ -189,21 +137,15 @@ spv_result_t ValidateEntryPoints(ValidationState_t& _) { "an OpFunctionCall instruction."; } - // For Vulkan and WebGPU, the static function-call graph for an entry point + // For Vulkan, the static function-call graph for an entry point // must not contain cycles. - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + 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)) << "Entry points may not have a call graph with cycles."; } } - - // For WebGPU all entry point names must be unique. - if (spvIsWebGPUEnv(_.context()->target_env)) { - const auto result = ValidateEntryPointNamesUnique(_); - if (result != SPV_SUCCESS) return result; - } } return SPV_SUCCESS; @@ -223,12 +165,6 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( << "Invalid SPIR-V magic number."; } - if (spvIsWebGPUEnv(context.target_env) && endian != SPV_ENDIANNESS_LITTLE) { - return DiagnosticStream(position, context.consumer, "", - SPV_ERROR_INVALID_BINARY) - << "WebGPU requires SPIR-V to be little endian."; - } - spv_header_t header; if (spvBinaryHeaderGet(binary.get(), endian, &header)) { return DiagnosticStream(position, context.consumer, "", @@ -321,13 +257,6 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( } const auto called_id = inst->GetOperandAs<uint32_t>(2); - if (spvIsWebGPUEnv(context.target_env) && - !vstate->IsFunctionCallDefined(called_id)) { - return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction) - << "For WebGPU, functions need to be defined before being " - "called."; - } - vstate->AddFunctionCallTarget(called_id); } diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index df38f1b1..85d2b751 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp @@ -22,36 +22,6 @@ namespace spvtools { namespace val { namespace { -bool IsValidWebGPUDecoration(uint32_t decoration) { - switch (decoration) { - case SpvDecorationSpecId: - case SpvDecorationBlock: - case SpvDecorationRowMajor: - case SpvDecorationColMajor: - case SpvDecorationArrayStride: - case SpvDecorationMatrixStride: - case SpvDecorationBuiltIn: - case SpvDecorationNoPerspective: - case SpvDecorationFlat: - case SpvDecorationCentroid: - case SpvDecorationRestrict: - case SpvDecorationAliased: - case SpvDecorationNonWritable: - case SpvDecorationNonReadable: - case SpvDecorationUniform: - case SpvDecorationLocation: - case SpvDecorationComponent: - case SpvDecorationIndex: - case SpvDecorationBinding: - case SpvDecorationDescriptorSet: - case SpvDecorationOffset: - case SpvDecorationNoContraction: - return true; - default: - return false; - } -} - std::string LogStringForDecoration(uint32_t decoration) { switch (decoration) { case SpvDecorationRelaxedPrecision: @@ -212,11 +182,14 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { } } - if (spvIsWebGPUEnv(_.context()->target_env) && - !IsValidWebGPUDecoration(decoration)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpDecorate decoration '" << LogStringForDecoration(decoration) - << "' is not valid for the WebGPU execution environment."; + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((decoration == SpvDecorationGLSLShared) || + (decoration == SpvDecorationGLSLPacked)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4669) << "OpDecorate decoration '" + << LogStringForDecoration(decoration) + << "' is not valid for the Vulkan execution environment."; + } } if (DecorationTakesIdParameters(decoration)) { @@ -261,25 +234,11 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _, << " members. Largest valid index is " << member_count - 1 << "."; } - const auto decoration = inst->GetOperandAs<uint32_t>(2); - if (spvIsWebGPUEnv(_.context()->target_env) && - !IsValidWebGPUDecoration(decoration)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpMemberDecorate decoration '" << _.getIdName(decoration) - << "' is not valid for the WebGPU execution environment."; - } - return SPV_SUCCESS; } spv_result_t ValidateDecorationGroup(ValidationState_t& _, const Instruction* inst) { - if (spvIsWebGPUEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_BINARY, inst) - << "OpDecorationGroup is not allowed in the WebGPU execution " - << "environment."; - } - const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0); const auto decoration_group = _.FindDef(decoration_group_id); for (auto pair : decoration_group->uses()) { @@ -299,12 +258,6 @@ spv_result_t ValidateDecorationGroup(ValidationState_t& _, spv_result_t ValidateGroupDecorate(ValidationState_t& _, const Instruction* inst) { - if (spvIsWebGPUEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_BINARY, inst) - << "OpGroupDecorate is not allowed in the WebGPU execution " - << "environment."; - } - const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0); auto decoration_group = _.FindDef(decoration_group_id); if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) { @@ -327,12 +280,6 @@ spv_result_t ValidateGroupDecorate(ValidationState_t& _, spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _, const Instruction* inst) { - if (spvIsWebGPUEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_BINARY, inst) - << "OpGroupMemberDecorate is not allowed in the WebGPU execution " - << "environment."; - } - const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0); const auto decoration_group = _.FindDef(decoration_group_id); if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) { diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp index 3f1f5617..dd263a79 100644 --- a/source/val/validate_atomics.cpp +++ b/source/val/validate_atomics.cpp @@ -213,7 +213,19 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { // Then Shader rules if (_.HasCapability(SpvCapabilityShader)) { - if (storage_class == SpvStorageClassFunction) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((storage_class != SpvStorageClassUniform) && + (storage_class != SpvStorageClassStorageBuffer) && + (storage_class != SpvStorageClassWorkgroup) && + (storage_class != SpvStorageClassImage) && + (storage_class != SpvStorageClassPhysicalStorageBuffer)) { + 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, or " + "PhysicalStorageBuffer."; + } + } else if (storage_class == SpvStorageClassFunction) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) << ": Function storage class forbidden when the Shader " diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp index 1d8a1406..3c9df9fc 100644 --- a/source/val/validate_builtins.cpp +++ b/source/val/validate_builtins.cpp @@ -113,28 +113,6 @@ SpvStorageClass GetStorageClass(const Instruction& inst) { return SpvStorageClassMax; } -bool IsBuiltInValidForWebGPU(SpvBuiltIn label) { - switch (label) { - case SpvBuiltInPosition: - case SpvBuiltInVertexIndex: - case SpvBuiltInInstanceIndex: - case SpvBuiltInFrontFacing: - case SpvBuiltInFragCoord: - case SpvBuiltInFragDepth: - case SpvBuiltInNumWorkgroups: - case SpvBuiltInWorkgroupSize: - case SpvBuiltInLocalInvocationId: - case SpvBuiltInGlobalInvocationId: - case SpvBuiltInLocalInvocationIndex: { - return true; - } - default: - break; - } - - return false; -} - typedef enum VUIDError_ { VUIDErrorExecutionModel = 0, VUIDErrorStorageClass = 1, @@ -142,15 +120,28 @@ typedef enum VUIDError_ { VUIDErrorMax, } VUIDError; -const static uint32_t NumRtBuiltins = 16; +const static uint32_t NumVUIDBuiltins = 33; typedef struct { SpvBuiltIn builtIn; uint32_t vuid[VUIDErrorMax]; // execution mode, storage class, type VUIDs -} RtBuiltinVUIDMapping; +} BuiltinVUIDMapping; -std::array<RtBuiltinVUIDMapping, NumRtBuiltins> rtBuiltinInfo = {{ +std::array<BuiltinVUIDMapping, NumVUIDBuiltins> builtinVUIDInfo = {{ // clang-format off + {SpvBuiltInSubgroupEqMask, {0, 4370, 4371}}, + {SpvBuiltInSubgroupGeMask, {0, 4372, 4373}}, + {SpvBuiltInSubgroupGtMask, {0, 4374, 4375}}, + {SpvBuiltInSubgroupLeMask, {0, 4376, 4377}}, + {SpvBuiltInSubgroupLtMask, {0, 4378, 4379}}, + {SpvBuiltInSubgroupLocalInvocationId, {0, 4380, 4381}}, + {SpvBuiltInSubgroupSize, {0, 4382, 4383}}, + {SpvBuiltInGlobalInvocationId, {4236, 4237, 4238}}, + {SpvBuiltInLocalInvocationId, {4281, 4282, 4283}}, + {SpvBuiltInNumWorkgroups, {4296, 4297, 4298}}, + {SpvBuiltInNumSubgroups, {4293, 4294, 4295}}, + {SpvBuiltInSubgroupId, {4367, 4368, 4369}}, + {SpvBuiltInWorkgroupId, {4422, 4423, 4424}}, {SpvBuiltInHitKindKHR, {4242, 4243, 4244}}, {SpvBuiltInHitTNV, {4245, 4246, 4247}}, {SpvBuiltInInstanceCustomIndexKHR, {4251, 4252, 4253}}, @@ -167,12 +158,16 @@ std::array<RtBuiltinVUIDMapping, NumRtBuiltins> rtBuiltinInfo = {{ {SpvBuiltInWorldRayOriginKHR, {4431, 4432, 4433}}, {SpvBuiltInLaunchIdKHR, {4266, 4267, 4268}}, {SpvBuiltInLaunchSizeKHR, {4269, 4270, 4271}}, + {SpvBuiltInFragInvocationCountEXT, {4217, 4218, 4219}}, + {SpvBuiltInFragSizeEXT, {4220, 4221, 4222}}, + {SpvBuiltInFragStencilRefEXT, {4223, 4224, 4225}}, + {SpvBuiltInFullyCoveredEXT, {4232, 4233, 4234}}, // clang-format off } }; -uint32_t GetVUIDForRTBuiltin(SpvBuiltIn builtIn, VUIDError type) { +uint32_t GetVUIDForBuiltin(SpvBuiltIn builtIn, VUIDError type) { uint32_t vuid = 0; - for (const auto& iter: rtBuiltinInfo) { + for (const auto& iter: builtinVUIDInfo) { if (iter.builtIn == builtIn) { assert(type < VUIDErrorMax); vuid = iter.vuid[type]; @@ -323,6 +318,14 @@ class BuiltInsValidator { 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); @@ -481,6 +484,26 @@ class BuiltInsValidator { 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, @@ -534,6 +557,9 @@ class BuiltInsValidator { spv_result_t ValidateBool( const Decoration& decoration, const Instruction& inst, const std::function<spv_result_t(const std::string& message)>& diag); + spv_result_t ValidateI( + const Decoration& decoration, const Instruction& inst, + const std::function<spv_result_t(const std::string& message)>& diag); spv_result_t ValidateI32( const Decoration& decoration, const Instruction& inst, const std::function<spv_result_t(const std::string& message)>& diag); @@ -726,6 +752,22 @@ spv_result_t BuiltInsValidator::ValidateBool( return SPV_SUCCESS; } +spv_result_t BuiltInsValidator::ValidateI( + const Decoration& decoration, const Instruction& inst, + const std::function<spv_result_t(const std::string& message)>& 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<spv_result_t(const std::string& message)>& diag) { @@ -1098,8 +1140,9 @@ spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference( if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput && storage_class != SpvStorageClassOutput) { + uint32_t vuid = (decoration.params()[0] == SpvBuiltInClipDistance) ? 4190 : 4199; return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) - << "Vulkan spec allows BuiltIn " + << _.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 " @@ -1111,19 +1154,28 @@ spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference( if (storage_class == SpvStorageClassInput) { assert(function_id_ == 0); + uint32_t vuid = (decoration.params()[0] == SpvBuiltInClipDistance) ? 4188 : 4197; id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( - &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, -1, + &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.", SpvExecutionModelVertex, 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 " + "Vertex.", + SpvExecutionModelMeshNV, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); } if (storage_class == SpvStorageClassOutput) { assert(function_id_ == 0); + uint32_t vuid = (decoration.params()[0] == SpvBuiltInClipDistance) ? 4189 : 4198; id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( - &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, -1, + &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.", @@ -1237,7 +1289,7 @@ spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference( spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { if (spv_result_t error = ValidateF32Vec( decoration, inst, 4, [this, &inst](const std::string& message) -> spv_result_t { @@ -1261,7 +1313,7 @@ spv_result_t BuiltInsValidator::ValidateFragCoordAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { @@ -1299,7 +1351,7 @@ spv_result_t BuiltInsValidator::ValidateFragCoordAtReference( spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { if (spv_result_t error = ValidateF32( decoration, inst, [this, &inst](const std::string& message) -> spv_result_t { @@ -1322,7 +1374,7 @@ spv_result_t BuiltInsValidator::ValidateFragDepthAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassOutput) { @@ -1375,7 +1427,7 @@ spv_result_t BuiltInsValidator::ValidateFragDepthAtReference( spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { if (spv_result_t error = ValidateBool( decoration, inst, [this, &inst](const std::string& message) -> spv_result_t { @@ -1398,7 +1450,7 @@ spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { @@ -1556,7 +1608,7 @@ spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference( spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { if (spv_result_t error = ValidateI32( decoration, inst, [this, &inst](const std::string& message) -> spv_result_t { @@ -1579,7 +1631,7 @@ spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { @@ -1869,7 +1921,7 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference( storage_class != SpvStorageClassInput && storage_class != SpvStorageClassOutput) { return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) - << "Vulkan spec allows BuiltIn Position to be only used for " + << _.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) @@ -1879,12 +1931,19 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference( if (storage_class == SpvStorageClassInput) { assert(function_id_ == 0); id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( - &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4320, + &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.", SpvExecutionModelVertex, 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.", + SpvExecutionModelMeshNV, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); } for (const SpvExecutionModel execution_model : execution_models_) { @@ -1962,46 +2021,6 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference( } } - if (spvIsWebGPUEnv(_.context()->target_env)) { - const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); - if (storage_class != SpvStorageClassMax && - storage_class != SpvStorageClassOutput) { - return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) - << "WebGPU spec allows BuiltIn Position 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 SpvExecutionModel execution_model : execution_models_) { - switch (execution_model) { - case SpvExecutionModelVertex: { - 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) - << "According to the WebGPU 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) - << "WebGPU spec allows BuiltIn Position to be used only " - "with the 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( @@ -2458,8 +2477,9 @@ spv_result_t BuiltInsValidator::ValidateTessLevelAtReference( if (storage_class == SpvStorageClassInput) { assert(function_id_ == 0); + uint32_t vuid = (decoration.params()[0] == SpvBuiltInTessLevelOuter) ? 4391 : 4395; id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( - &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, -1, + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " "used " "for variables with Input storage class if execution model is " @@ -2470,8 +2490,9 @@ spv_result_t BuiltInsValidator::ValidateTessLevelAtReference( if (storage_class == SpvStorageClassOutput) { assert(function_id_ == 0); + uint32_t vuid = (decoration.params()[0] == SpvBuiltInTessLevelOuter) ? 4392 : 4396; id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( - &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, -1, + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid, "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " "used " "for variables with Output storage class if execution model is " @@ -2515,7 +2536,7 @@ spv_result_t BuiltInsValidator::ValidateTessLevelAtReference( spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { if (spv_result_t error = ValidateI32( decoration, inst, [this, &inst](const std::string& message) -> spv_result_t { @@ -2548,51 +2569,14 @@ spv_result_t BuiltInsValidator::ValidateVertexIdAtDefinition( spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsWebGPUEnv(_.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) - << "According to the WebGPU spec BuiltIn " - "LocalInvocationIndex variable needs to be a 32-bit " - "int." - << message; - })) { - return error; - } - } - // 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& referenced_inst, + const Instruction&, const Instruction& referenced_from_inst) { - if (spvIsWebGPUEnv(_.context()->target_env)) { - const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); - if (storage_class != SpvStorageClassMax && - storage_class != SpvStorageClassInput) { - return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) - << "WebGPU spec allows BuiltIn LocalInvocationIndex 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 SpvExecutionModel execution_model : execution_models_) { - if (execution_model != SpvExecutionModelGLCompute) { - return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) - << "WebGPU spec allows BuiltIn VertexIndex to be used only " - "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( @@ -2608,7 +2592,7 @@ spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { @@ -2768,8 +2752,9 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( if (operand == SpvBuiltInLayer) capability = "ShaderViewportIndexLayerEXT or ShaderLayer"; + uint32_t vuid = (operand == SpvBuiltInLayer) ? 4273 : 4405; return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) - << "Using BuiltIn " + << _.VkErrorID(vuid) << "Using BuiltIn " << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, operand) << " in Vertex or Tessellation execution model requires the " @@ -2805,33 +2790,18 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); if (spv_result_t error = ValidateI32Vec( decoration, inst, 3, - [this, &decoration, - &inst](const std::string& message) -> spv_result_t { - uint32_t operand = decoration.params()[0]; - uint32_t vuid = 0; - switch (operand) { - case SpvBuiltInGlobalInvocationId: - vuid = 4238; - break; - case SpvBuiltInLocalInvocationId: - vuid = 4283; - break; - case SpvBuiltInNumWorkgroups: - vuid = 4298; - break; - case SpvBuiltInWorkgroupId: - vuid = 4424; - break; - }; + [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, - operand) + builtin) << " variable needs to be a 3-component 32-bit int " "vector. " << message; @@ -2849,31 +2819,16 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst) { - uint32_t operand = decoration.params()[0]; - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { - uint32_t vuid = 0; - switch (operand) { - case SpvBuiltInGlobalInvocationId: - vuid = 4237; - break; - case SpvBuiltInLocalInvocationId: - vuid = 4282; - break; - case SpvBuiltInNumWorkgroups: - vuid = 4297; - break; - case SpvBuiltInWorkgroupId: - vuid = 4423; - break; - }; + 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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " to be only used for variables with Input storage class. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst) @@ -2884,30 +2839,13 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute || execution_model == SpvExecutionModelTaskNV || execution_model == SpvExecutionModelMeshNV; - bool has_webgpu_model = execution_model == SpvExecutionModelGLCompute; - if ((spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) || - (spvIsWebGPUEnv(_.context()->target_env) && !has_webgpu_model)) { - uint32_t vuid = 0; - switch (operand) { - case SpvBuiltInGlobalInvocationId: - vuid = 4236; - break; - case SpvBuiltInLocalInvocationId: - vuid = 4281; - break; - case SpvBuiltInNumWorkgroups: - vuid = 4296; - break; - case SpvBuiltInWorkgroupId: - vuid = 4422; - break; - }; + 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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " to be used only with GLCompute execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); @@ -2929,23 +2867,23 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( spv_result_t BuiltInsValidator::ValidateComputeI32InputAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); if (decoration.struct_member_index() != Decoration::kInvalidMember) { return _.diag(SPV_ERROR_INVALID_DATA, &inst) << "BuiltIn " - << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " cannot be used as a member decoration "; } if (spv_result_t error = ValidateI32( decoration, inst, - [this, &decoration, - &inst](const std::string& message) -> spv_result_t { + [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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " variable needs to be a 32-bit int " "vector. " << message; @@ -2963,14 +2901,16 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( const Instruction& referenced_inst, const Instruction& referenced_from_inst) { if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { + 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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " to be only used for variables with Input storage class. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst) @@ -2982,11 +2922,12 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( execution_model == SpvExecutionModelTaskNV || execution_model == SpvExecutionModelMeshNV; 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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " to be used only with GLCompute execution model. " << GetReferenceDesc(decoration, built_in_inst, referenced_inst, referenced_from_inst, execution_model); @@ -3008,23 +2949,23 @@ spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); if (decoration.struct_member_index() != Decoration::kInvalidMember) { return _.diag(SPV_ERROR_INVALID_DATA, &inst) << "BuiltIn " - << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " cannot be used as a member decoration "; } if (spv_result_t error = ValidateI32( decoration, inst, - [this, &decoration, - &inst](const std::string& message) -> spv_result_t { + [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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " variable needs to be a 32-bit int. " << message; })) { return error; @@ -3033,11 +2974,12 @@ spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition( const SpvStorageClass storage_class = GetStorageClass(inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { + 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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " to be only used for variables with Input storage class. " << GetReferenceDesc(decoration, inst, inst, inst) << " " << GetStorageClassDesc(inst); @@ -3050,23 +2992,23 @@ spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition( spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); if (decoration.struct_member_index() != Decoration::kInvalidMember) { return _.diag(SPV_ERROR_INVALID_DATA, &inst) << "BuiltIn " - << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " cannot be used as a member decoration "; } if (spv_result_t error = ValidateI32Vec( decoration, inst, 4, - [this, &decoration, - &inst](const std::string& message) -> spv_result_t { + [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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " variable needs to be a 4-component 32-bit int " "vector. " << message; @@ -3077,11 +3019,12 @@ spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition( const SpvStorageClass storage_class = GetStorageClass(inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { + 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, - decoration.params()[0]) + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin) << " to be only used for variables with Input storage class. " << GetReferenceDesc(decoration, inst, inst, inst) << " " << GetStorageClassDesc(inst); @@ -3093,7 +3036,7 @@ spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition( spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition( const Decoration& decoration, const Instruction& inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { if (spvIsVulkanEnv(_.context()->target_env) && !spvOpcodeIsConstant(inst.opcode())) { return _.diag(SPV_ERROR_INVALID_DATA, &inst) @@ -3125,7 +3068,7 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference( const Decoration& decoration, const Instruction& built_in_inst, const Instruction& referenced_inst, const Instruction& referenced_from_inst) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env)) { for (const SpvExecutionModel execution_model : execution_models_) { if (execution_model != SpvExecutionModelGLCompute) { return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) @@ -3404,6 +3347,287 @@ spv_result_t BuiltInsValidator::ValidateDeviceIndexAtReference( return SPV_SUCCESS; } +spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtDefinition(const Decoration& decoration, + const Instruction& inst) { + + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + 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, + 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 SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + 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, 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 SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + 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, 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 SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + 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, + 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 SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + 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, 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 SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + 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, 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 SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + 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, + 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 SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassOutput) { + 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, 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 SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + 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, 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 SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + 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, + 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 SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]); + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + 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, 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 SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + 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, 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::ValidateSMBuiltinsAtDefinition( const Decoration& decoration, const Instruction& inst) { if (spvIsVulkanEnv(_.context()->target_env)) { @@ -3608,7 +3832,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition( decoration, inst, [this, &inst, builtin](const std::string& message) -> spv_result_t { - uint32_t vuid = GetVUIDForRTBuiltin(builtin, VUIDErrorType); + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); return _.diag(SPV_ERROR_INVALID_DATA, &inst) << _.VkErrorID(vuid) << "According to the Vulkan spec BuiltIn " @@ -3630,7 +3854,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition( decoration, inst, [this, &inst, builtin](const std::string& message) -> spv_result_t { - uint32_t vuid = GetVUIDForRTBuiltin(builtin, VUIDErrorType); + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); return _.diag(SPV_ERROR_INVALID_DATA, &inst) << _.VkErrorID(vuid) << "According to the Vulkan spec BuiltIn " @@ -3651,7 +3875,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition( decoration, inst, 3, [this, &inst, builtin](const std::string& message) -> spv_result_t { - uint32_t vuid = GetVUIDForRTBuiltin(builtin, VUIDErrorType); + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); return _.diag(SPV_ERROR_INVALID_DATA, &inst) << _.VkErrorID(vuid) << "According to the Vulkan spec BuiltIn " @@ -3671,7 +3895,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition( decoration, inst, 3, [this, &inst, builtin](const std::string& message) -> spv_result_t { - uint32_t vuid = GetVUIDForRTBuiltin(builtin, VUIDErrorType); + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); return _.diag(SPV_ERROR_INVALID_DATA, &inst) << _.VkErrorID(vuid) << "According to the Vulkan spec BuiltIn " @@ -3691,7 +3915,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition( decoration, inst, 3, 4, [this, &inst, builtin](const std::string& message) -> spv_result_t { - uint32_t vuid = GetVUIDForRTBuiltin(builtin, VUIDErrorType); + uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType); return _.diag(SPV_ERROR_INVALID_DATA, &inst) << _.VkErrorID(vuid) << "According to the Vulkan spec BuiltIn " @@ -3724,7 +3948,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtReference( const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); if (storage_class != SpvStorageClassMax && storage_class != SpvStorageClassInput) { - uint32_t vuid = GetVUIDForRTBuiltin(builtin, VUIDErrorStorageClass); + 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, @@ -3737,7 +3961,7 @@ spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtReference( for (const SpvExecutionModel execution_model : execution_models_) { if (!IsExecutionModelValidForRtBuiltIn(builtin, execution_model)) { - uint32_t vuid = GetVUIDForRTBuiltin(builtin, VUIDErrorExecutionModel); + 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, @@ -3775,26 +3999,17 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( << "BuiltIns can only target variables, structs or constants"; } - if (!spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { - // Early return. All currently implemented rules are based on Vulkan or - // WebGPU spec. + if (!spvIsVulkanEnv(_.context()->target_env)) { + // Early return. All currently implemented rules are based on Vulkan spec. // // TODO: If you are adding validation rules for environments other than - // Vulkan or WebGPU (or general rules which are not environment - // independent), then you need to modify or remove this condition. Consider - // also adding early returns into BuiltIn-specific rules, so that the system - // doesn't spawn new rules which don't do anything. + // Vulkan (or general rules which are not environment independent), then + // you need to modify or remove this condition. Consider also adding early + // returns into BuiltIn-specific rules, so that the system doesn't spawn new + // rules which don't do anything. return SPV_SUCCESS; } - if (spvIsWebGPUEnv(_.context()->target_env) && - !IsBuiltInValidForWebGPU(label)) { - return _.diag(SPV_ERROR_INVALID_DATA, &inst) - << "WebGPU does not allow BuiltIn " - << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, - decoration.params()[0]); - } - // 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. @@ -3910,6 +4125,20 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInDeviceIndex: { return ValidateDeviceIndexAtDefinition(decoration, inst); } + case SpvBuiltInFragInvocationCountEXT: { + // alias SpvBuiltInInvocationsPerPixelNV + return ValidateFragInvocationCountAtDefinition(decoration, inst); + } + case SpvBuiltInFragSizeEXT: { + // alias SpvBuiltInFragmentSizeNV + return ValidateFragSizeAtDefinition(decoration, inst); + } + case SpvBuiltInFragStencilRefEXT: { + return ValidateFragStencilRefAtDefinition(decoration, inst); + } + case SpvBuiltInFullyCoveredEXT:{ + return ValidateFullyCoveredAtDefinition(decoration, inst); + } // Ray tracing builtins case SpvBuiltInHitKindKHR: // alias SpvBuiltInHitKindNV case SpvBuiltInHitTNV: // NOT present in KHR @@ -3945,13 +4174,11 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInBaryCoordSmoothCentroidAMD: case SpvBuiltInBaryCoordSmoothSampleAMD: case SpvBuiltInBaryCoordPullModelAMD: - case SpvBuiltInFragStencilRefEXT: case SpvBuiltInViewportMaskNV: case SpvBuiltInSecondaryPositionNV: case SpvBuiltInSecondaryViewportMaskNV: case SpvBuiltInPositionPerViewNV: case SpvBuiltInViewportMaskPerViewNV: - case SpvBuiltInFullyCoveredEXT: case SpvBuiltInMax: case SpvBuiltInTaskCountNV: case SpvBuiltInPrimitiveCountNV: @@ -3963,9 +4190,6 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( case SpvBuiltInMeshViewIndicesNV: case SpvBuiltInBaryCoordNV: case SpvBuiltInBaryCoordNoPerspNV: - case SpvBuiltInFragmentSizeNV: // alias SpvBuiltInFragSizeEXT - case SpvBuiltInInvocationsPerPixelNV: // alias - // SpvBuiltInFragInvocationCountEXT // No validation rules (for the moment). break; diff --git a/source/val/validate_capability.cpp b/source/val/validate_capability.cpp index 4b98bc19..8efd5542 100644 --- a/source/val/validate_capability.cpp +++ b/source/val/validate_capability.cpp @@ -260,19 +260,6 @@ bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _, return false; } -bool IsSupportGuaranteedWebGPU(uint32_t capability) { - switch (capability) { - case SpvCapabilityMatrix: - case SpvCapabilityShader: - case SpvCapabilitySampled1D: - case SpvCapabilityImage1D: - case SpvCapabilityDerivativeControl: - case SpvCapabilityImageQuery: - return true; - } - return false; -} - } // namespace // Validates that capability declarations use operands allowed in the current @@ -365,14 +352,6 @@ spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) { << " Profile specification" << " (or requires extension or capability)"; } - } else if (env == SPV_ENV_WEBGPU_0) { - if (!IsSupportGuaranteedWebGPU(capability) && - !IsEnabledByExtension(_, capability)) { - return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) - << "Capability " << capability_str() - << " is not allowed by WebGPU specification" - << " (or requires extension)"; - } } return SPV_SUCCESS; diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index ca2ae1ae..a5f6e6a1 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -820,120 +820,6 @@ spv_result_t StructuredControlFlowChecks( return SPV_SUCCESS; } -spv_result_t PerformWebGPUCfgChecks(ValidationState_t& _, Function* function) { - for (auto& block : function->ordered_blocks()) { - if (block->reachable()) continue; - if (block->is_type(kBlockTypeMerge)) { - // 1. Find the referencing merge and confirm that it is reachable. - BasicBlock* merge_header = function->GetMergeHeader(block); - assert(merge_header != nullptr); - if (!merge_header->reachable()) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable merge-blocks must be referenced by " - "a reachable merge instruction."; - } - - // 2. Check that the only instructions are OpLabel and OpUnreachable. - auto* label_inst = block->label(); - auto* terminator_inst = block->terminator(); - assert(label_inst != nullptr); - assert(terminator_inst != nullptr); - - if (terminator_inst->opcode() != SpvOpUnreachable) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable merge-blocks must terminate with " - "OpUnreachable."; - } - - auto label_idx = label_inst - &_.ordered_instructions()[0]; - auto terminator_idx = terminator_inst - &_.ordered_instructions()[0]; - if (label_idx + 1 != terminator_idx) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable merge-blocks must only contain an " - "OpLabel and OpUnreachable instruction."; - } - - // 3. Use label instruction to confirm there is no uses by branches. - for (auto use : label_inst->uses()) { - const auto* use_inst = use.first; - if (spvOpcodeIsBranch(use_inst->opcode())) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable merge-blocks cannot be the target " - "of a branch."; - } - } - } else if (block->is_type(kBlockTypeContinue)) { - // 1. Find referencing loop and confirm that it is reachable. - std::vector<BasicBlock*> continue_headers = - function->GetContinueHeaders(block); - if (continue_headers.empty()) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable continue-target must be referenced " - "by a loop instruction."; - } - - std::vector<BasicBlock*> reachable_headers(continue_headers.size()); - auto iter = - std::copy_if(continue_headers.begin(), continue_headers.end(), - reachable_headers.begin(), - [](BasicBlock* header) { return header->reachable(); }); - reachable_headers.resize(std::distance(reachable_headers.begin(), iter)); - - if (reachable_headers.empty()) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable continue-target must be referenced " - "by a reachable loop instruction."; - } - - // 2. Check that the only instructions are OpLabel and OpBranch. - auto* label_inst = block->label(); - auto* terminator_inst = block->terminator(); - assert(label_inst != nullptr); - assert(terminator_inst != nullptr); - - if (terminator_inst->opcode() != SpvOpBranch) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable continue-target must terminate with " - "OpBranch."; - } - - auto label_idx = label_inst - &_.ordered_instructions()[0]; - auto terminator_idx = terminator_inst - &_.ordered_instructions()[0]; - if (label_idx + 1 != terminator_idx) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable continue-target must only contain " - "an OpLabel and an OpBranch instruction."; - } - - // 3. Use label instruction to confirm there is no uses by branches. - for (auto use : label_inst->uses()) { - const auto* use_inst = use.first; - if (spvOpcodeIsBranch(use_inst->opcode())) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable continue-target cannot be the " - "target of a branch."; - } - } - - // 4. Confirm that continue-target has a back edge to a reachable loop - // header block. - auto branch_target = terminator_inst->GetOperandAs<uint32_t>(0); - for (auto* continue_header : reachable_headers) { - if (branch_target != continue_header->id()) { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, unreachable continue-target must only have a " - "back edge to a single reachable loop instruction."; - } - } - } else { - return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) - << "For WebGPU, all blocks must be reachable, unless they are " - << "degenerate cases of merge-block or continue-target."; - } - } - return SPV_SUCCESS; -} - spv_result_t PerformCfgChecks(ValidationState_t& _) { for (auto& function : _.functions()) { // Check all referenced blocks are defined within a function @@ -1014,13 +900,6 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { << _.getIdName(idom->id()); } } - - // For WebGPU check that all unreachable blocks are degenerate cases for - // merge-block or continue-target. - if (spvIsWebGPUEnv(_.context()->target_env)) { - spv_result_t result = PerformWebGPUCfgChecks(_, &function); - if (result != SPV_SUCCESS) return result; - } } // If we have structed control flow, check that no block has a control // flow nesting depth larger than the limit. diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp index d5b978ff..5d6c5e37 100644 --- a/source/val/validate_composites.cpp +++ b/source/val/validate_composites.cpp @@ -535,12 +535,10 @@ spv_result_t ValidateVectorShuffle(ValidationState_t& _, } // All Component literals must either be FFFFFFFF or in [0, N - 1]. - // For WebGPU specifically, Component literals cannot be FFFFFFFF. auto vector1ComponentCount = vector1Type->GetOperandAs<uint32_t>(2); auto vector2ComponentCount = vector2Type->GetOperandAs<uint32_t>(2); auto N = vector1ComponentCount + vector2ComponentCount; auto firstLiteralIndex = 4; - const auto is_webgpu_env = spvIsWebGPUEnv(_.context()->target_env); for (size_t i = firstLiteralIndex; i < inst->operands().size(); ++i) { auto literal = inst->GetOperandAs<uint32_t>(i); if (literal != 0xFFFFFFFF && literal >= N) { @@ -548,12 +546,6 @@ spv_result_t ValidateVectorShuffle(ValidationState_t& _, << "Component index " << literal << " is out of bounds for " << "combined (Vector1 + Vector2) size of " << N << "."; } - - if (is_webgpu_env && literal == 0xFFFFFFFF) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Component literal at operand " << i - firstLiteralIndex - << " cannot be 0xFFFFFFFF in WebGPU execution environment."; - } } if (_.HasCapability(SpvCapabilityShader) && diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp index 0060d0b7..b4e39cfe 100644 --- a/source/val/validate_conversion.cpp +++ b/source/val/validate_conversion.cpp @@ -14,12 +14,12 @@ // Validates correctness of conversion instructions. -#include "source/val/validate.h" - #include "source/diagnostic.h" #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 { @@ -263,16 +263,25 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { << "Logical addressing not supported: " << spvOpcodeString(opcode); - if (_.addressing_model() == - SpvAddressingModelPhysicalStorageBuffer64EXT) { + if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) { uint32_t input_storage_class = 0; uint32_t input_data_type = 0; _.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class); - if (input_storage_class != SpvStorageClassPhysicalStorageBufferEXT) + if (input_storage_class != SpvStorageClassPhysicalStorageBuffer) return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Pointer storage class must be PhysicalStorageBufferEXT: " + << "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; } @@ -314,16 +323,25 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { << "Logical addressing not supported: " << spvOpcodeString(opcode); - if (_.addressing_model() == - SpvAddressingModelPhysicalStorageBuffer64EXT) { + if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) { uint32_t result_storage_class = 0; uint32_t result_data_type = 0; _.GetPointerTypeInfo(result_type, &result_data_type, &result_storage_class); - if (result_storage_class != SpvStorageClassPhysicalStorageBufferEXT) + if (result_storage_class != SpvStorageClassPhysicalStorageBuffer) return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Pointer storage class must be PhysicalStorageBufferEXT: " + << "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; } diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index d3812766..ed336b47 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -379,6 +379,7 @@ bool IsAlignedTo(uint32_t offset, uint32_t alignment) { // 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) { @@ -392,7 +393,6 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, // 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(); - const bool scalar_block_layout = vstate.options()->scalar_block_layout; auto fail = [&vstate, struct_id, storage_class_str, decoration_str, blockRules, relaxed_block_layout, @@ -501,6 +501,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, if (SpvOpTypeStruct == 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. @@ -553,7 +554,8 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, if (SpvOpTypeStruct == element_inst->opcode() && SPV_SUCCESS != (recursive_status = checkLayout( typeId, storage_class_str, decoration_str, - blockRules, next_offset, constraints, vstate))) + blockRules, scalar_block_layout, + next_offset, constraints, vstate))) return recursive_status; // If offsets accumulate up to a 16-byte multiple stop checking since // it will just repeat. @@ -698,6 +700,9 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { const auto& descs = vstate.entry_point_descriptions(entry_point); int num_builtin_inputs = 0; int num_builtin_outputs = 0; + int num_workgroup_variables = 0; + int num_workgroup_variables_with_block = 0; + int num_workgroup_variables_with_aliased = 0; for (const auto& desc : descs) { std::unordered_set<Instruction*> seen_vars; for (auto interface : desc.interfaces) { @@ -754,6 +759,16 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { if (auto error = CheckBuiltInVariable(interface, vstate)) return error; } + + if (storage_class == SpvStorageClassWorkgroup) { + ++num_workgroup_variables; + if (type_instr && SpvOpTypeStruct == type_instr->opcode()) { + if (hasDecoration(type_id, SpvDecorationBlock, vstate)) + ++num_workgroup_variables_with_block; + if (hasDecoration(var_instr->id(), SpvDecorationAliased, vstate)) + ++num_workgroup_variables_with_aliased; + } + } } if (num_builtin_inputs > 1 || num_builtin_outputs > 1) { return vstate.diag(SPV_ERROR_INVALID_BINARY, @@ -777,6 +792,30 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { << " because it is targeted by an OpEntryPoint instruction."; } } + + if (vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR) && + 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. 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. " + "Entry point id " + << entry_point << " does not meet this requirement."; + } + } } } return SPV_SUCCESS; @@ -942,14 +981,16 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { const bool phys_storage_buffer = storageClass == SpvStorageClassPhysicalStorageBufferEXT; - if (uniform || push_constant || storage_buffer || phys_storage_buffer) { + const bool workgroup = storageClass == SpvStorageClassWorkgroup; + if (uniform || push_constant || storage_buffer || phys_storage_buffer || + workgroup) { const auto ptrInst = vstate.FindDef(words[1]); assert(SpvOpTypePointer == ptrInst->opcode()); auto id = ptrInst->words()[3]; auto id_inst = vstate.FindDef(id); // Jump through one level of arraying. - if (id_inst->opcode() == SpvOpTypeArray || - id_inst->opcode() == SpvOpTypeRuntimeArray) { + if (!workgroup && (id_inst->opcode() == SpvOpTypeArray || + id_inst->opcode() == SpvOpTypeRuntimeArray)) { id = id_inst->GetOperandAs<uint32_t>(1u); id_inst = vstate.FindDef(id); } @@ -961,7 +1002,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { // Prepare for messages const char* sc_str = uniform ? "Uniform" - : (push_constant ? "PushConstant" : "StorageBuffer"); + : (push_constant ? "PushConstant" + : (workgroup ? "Workgroup" + : "StorageBuffer")); if (spvIsVulkanEnv(vstate.context()->target_env)) { const bool block = hasDecoration(id, SpvDecorationBlock, vstate); @@ -1029,8 +1072,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type(); const bool blockRules = uniform && blockDeco; const bool bufferRules = - (uniform && bufferDeco) || (push_constant && blockDeco) || - ((storage_buffer || phys_storage_buffer) && blockDeco); + (uniform && bufferDeco) || + ((push_constant || storage_buffer || + phys_storage_buffer || workgroup) && blockDeco); if (uniform && blockDeco) { vstate.RegisterPointerToUniformBlock(ptrInst->id()); vstate.RegisterStructForUniformBlock(id); @@ -1044,6 +1088,10 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { if (blockRules || bufferRules) { const char* deco_str = blockDeco ? "Block" : "BufferBlock"; spv_result_t recursive_status = SPV_SUCCESS; + const bool 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 @@ -1072,12 +1120,14 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { "decorations."; } else if (blockRules && (SPV_SUCCESS != (recursive_status = checkLayout( - id, sc_str, deco_str, true, 0, + 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, 0, + id, sc_str, deco_str, false, + scalar_block_layout, 0, constraints, vstate)))) { return recursive_status; } @@ -1260,7 +1310,8 @@ spv_result_t CheckVulkanMemoryModelDeprecatedDecorations( // decorations. Otherwise emits a diagnostic and returns something other than // SPV_SUCCESS. spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate, - const Instruction& inst) { + const Instruction& inst, + const Decoration& decoration) { // Validates width-only conversion instruction for floating-point object // i.e., OpFConvert if (inst.opcode() != SpvOpFConvert) { @@ -1270,6 +1321,15 @@ spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate, "object."; } + if (spvIsVulkanEnv(vstate.context()->target_env)) { + const auto mode = decoration.params()[0]; + if ((mode != SpvFPRoundingModeRTE) && (mode != SpvFPRoundingModeRTZ)) { + 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; @@ -1588,7 +1648,8 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { break; case SpvDecorationFPRoundingMode: if (is_shader) - PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst)); + PASS_OR_BAIL( + CheckFPRoundingModeForShaders(vstate, *inst, decoration)); break; case SpvDecorationNonWritable: PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration)); diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index 17b04460..dc8c0243 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -27,6 +27,7 @@ #include "source/latest_version_glsl_std_450_header.h" #include "source/latest_version_opencl_std_header.h" #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" @@ -686,14 +687,13 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _, } // anonymous namespace spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { - if (spvIsWebGPUEnv(_.context()->target_env)) { + if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) { std::string extension = GetExtensionString(&(inst->c_inst())); - - if (extension != ExtensionToString(kSPV_KHR_vulkan_memory_model)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For WebGPU, the only valid parameter to OpExtension is " - << "\"" << ExtensionToString(kSPV_KHR_vulkan_memory_model) - << "\"."; + if (extension == + ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << "SPV_KHR_workgroup_memory_explicit_layout extension " + "requires SPIR-V version 1.4 or later."; } } @@ -703,16 +703,6 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { spv_result_t ValidateExtInstImport(ValidationState_t& _, const Instruction* inst) { const auto name_id = 1; - if (spvIsWebGPUEnv(_.context()->target_env)) { - const std::string name(reinterpret_cast<const char*>( - inst->words().data() + inst->operands()[name_id].offset)); - if (name != "GLSL.std.450") { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For WebGPU, the only valid parameter to OpExtInstImport is " - "\"GLSL.std.450\"."; - } - } - if (!_.HasExtension(kSPV_KHR_non_semantic_info)) { const std::string name(reinterpret_cast<const char*>( inst->words().data() + inst->operands()[name_id].offset)); diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp index e1a775a8..2bab2034 100644 --- a/source/val/validate_id.cpp +++ b/source/val/validate_id.cpp @@ -201,7 +201,7 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) { ret = SPV_SUCCESS; } } else if (can_have_forward_declared_ids(i)) { - if (inst->opcode() == SpvOpTypeStruct && + if (spvOpcodeGeneratesType(inst->opcode()) && !_.IsForwardPointer(operand_word)) { ret = _.diag(SPV_ERROR_INVALID_ID, inst) << "Operand " << _.getIdName(operand_word) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index 299a3efc..bdb2516e 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -16,8 +16,6 @@ // Validates correctness of image instructions. -#include "source/val/validate.h" - #include <string> #include "source/diagnostic.h" @@ -25,6 +23,7 @@ #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" @@ -234,9 +233,10 @@ uint32_t GetMinCoordSize(SpvOp opcode, const ImageTypeInfo& info) { } // 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 mask, + const ImageTypeInfo& info, uint32_t word_index) { static const bool kAllImageOperandsHandled = CheckAllImageOperandsHandled(); (void)kAllImageOperandsHandled; @@ -244,28 +244,48 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, const SpvOp opcode = inst->opcode(); const size_t num_words = inst->words().size(); - // NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words. - const uint32_t mask_bits_having_operands = - mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask | - SpvImageOperandsVolatileTexelKHRMask | - SpvImageOperandsSignExtendMask | - SpvImageOperandsZeroExtendMask); - size_t expected_num_image_operand_words = - spvtools::utils::CountSetBits(mask_bits_having_operands); - if (mask & SpvImageOperandsGradMask) { - // Grad uses two words. - ++expected_num_image_operand_words; - } + 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(SpvImageOperandsNonPrivateTexelKHRMask | + SpvImageOperandsVolatileTexelKHRMask | + SpvImageOperandsSignExtendMask | + SpvImageOperandsZeroExtendMask); + size_t expected_num_image_operand_words = + spvtools::utils::CountSetBits(mask_bits_having_operands); + if (mask & SpvImageOperandsGradMask) { + // Grad uses two words. + ++expected_num_image_operand_words; + } - if (expected_num_image_operand_words != num_words - word_index) { + 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 & SpvImageOperandsSampleMask))) { + 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 & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask | SpvImageOperandsConstOffsetsMask)) > 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4662) << "Image Operands Offset, ConstOffset, ConstOffsets cannot be used " << "together"; } @@ -296,10 +316,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, "or Cube"; } - if (info.multisampled != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Image Operand Bias requires 'MS' parameter to be 0"; - } + // Multisampled is already checked. } if (mask & SpvImageOperandsLodMask) { @@ -338,10 +355,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, "or Cube"; } - if (info.multisampled != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Image Operand Lod requires 'MS' parameter to be 0"; - } + // Multisampled is already checked. } if (mask & SpvImageOperandsGradMask) { @@ -374,10 +388,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, << " components, but given " << dy_size; } - if (info.multisampled != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Image Operand Grad requires 'MS' parameter to be 0"; - } + // Multisampled is already checked. } if (mask & SpvImageOperandsConstOffsetMask) { @@ -430,6 +441,17 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, << "Expected Image Operand Offset to have " << plane_size << " components, but given " << offset_size; } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather && + opcode != SpvOpImageSparseGather && + opcode != SpvOpImageSparseDrefGather) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4663) + << "Image Operand Offset can only be used with " + "OpImage*Gather operations"; + } + } } if (mask & SpvImageOperandsConstOffsetsMask) { @@ -613,12 +635,12 @@ spv_result_t ValidateImageCommon(ValidationState_t& _, const Instruction* inst, if (info.multisampled != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Image Image 'MS' parameter to be 0"; + << "Expected Image 'MS' parameter to be 0"; } if (info.arrayed != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Image Image 'arrayed' parameter to be 0"; + << "Expected Image 'arrayed' parameter to be 0"; } } @@ -736,17 +758,28 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { << "Corrupt image type definition"; } - if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.IsIntScalarType(info.sampled_type) && + (64 == _.GetBitWidth(info.sampled_type)) && + !_.HasCapability(SpvCapabilityInt64ImageEXT)) { + 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) || - !_.HasCapability(SpvCapabilityInt64ImageEXT)))) { + ((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) - << "Expected Sampled Type to be a 32-bit int or float " - "scalar type for Vulkan environment"; + << _.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(_.context()->target_env)) { + } 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."; @@ -774,7 +807,7 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)"; } - if (spvIsOpenCLEnv(_.context()->target_env)) { + if (spvIsOpenCLEnv(target_env)) { if ((info.arrayed == 1) && (info.dim != SpvDim1D) && (info.dim != SpvDim2D)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -788,10 +821,10 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { << "Invalid MS " << info.multisampled << " (must be 0 or 1)"; } - if (spvIsOpenCLEnv(_.context()->target_env)) { + if (spvIsOpenCLEnv(target_env)) { if (info.multisampled != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "MS must be 0 in the OpenCL environement."; + << "MS must be 0 in the OpenCL environment."; } } @@ -800,6 +833,14 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)"; } + 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 (spvIsOpenCLEnv(_.context()->target_env)) { if (info.sampled != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -829,6 +870,15 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { } } + if (info.multisampled && (info.sampled == 2) && + (info.dim != SpvDimSubpassData)) { + if (!_.HasCapability(SpvCapabilityStorageImageMultisample)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability StorageImageMultisample is required when using " + "multisampled storage image"; + } + } + return SPV_SUCCESS; } @@ -839,6 +889,21 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _, 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 ((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"; + } + return SPV_SUCCESS; } @@ -1066,6 +1131,20 @@ spv_result_t ValidateImageTexelPointer(ValidationState_t& _, "the value 0"; } } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((info.format != SpvImageFormatR64i) && + (info.format != SpvImageFormatR64ui) && + (info.format != SpvImageFormatR32f) && + (info.format != SpvImageFormatR32i) && + (info.format != SpvImageFormatR32ui)) { + 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; } @@ -1103,6 +1182,14 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { if (spv_result_t result = ValidateImageCommon(_, 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) != SpvOpTypeVoid) { const uint32_t texel_component_type = _.GetComponentType(actual_result_type); @@ -1137,16 +1224,11 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { << " components, but given only " << actual_coord_size; } - if (inst->words().size() <= 5) { - assert(IsImplicitLod(opcode)); - return SPV_SUCCESS; - } - - const uint32_t mask = inst->word(5); + const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); - if (spvIsOpenCLEnv(_.context()->target_env)) { - if (opcode == SpvOpImageSampleExplicitLod) { - if (mask & SpvImageOperandsConstOffsetMask) { + if (mask & SpvImageOperandsConstOffsetMask) { + if (spvIsOpenCLEnv(_.context()->target_env)) { + if (opcode == SpvOpImageSampleExplicitLod) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "ConstOffset image operand not allowed " << "in the OpenCL environment."; @@ -1155,7 +1237,7 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { } if (spv_result_t result = - ValidateImageOperands(_, inst, info, mask, /* word_index = */ 6)) + ValidateImageOperands(_, inst, info, /* word_index = */ 6)) return result; return SPV_SUCCESS; @@ -1190,6 +1272,14 @@ spv_result_t ValidateImageDrefLod(ValidationState_t& _, if (spv_result_t result = ValidateImageCommon(_, 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 " @@ -1216,14 +1306,8 @@ spv_result_t ValidateImageDrefLod(ValidationState_t& _, << "Expected Dref to be of 32-bit float type"; } - if (inst->words().size() <= 6) { - assert(IsImplicitLod(opcode)); - return SPV_SUCCESS; - } - - const uint32_t mask = inst->word(6); if (spv_result_t result = - ValidateImageOperands(_, inst, info, mask, /* word_index = */ 7)) + ValidateImageOperands(_, inst, info, /* word_index = */ 7)) return result; return SPV_SUCCESS; @@ -1294,11 +1378,8 @@ spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) { << " components, but given only " << actual_coord_size; } - if (inst->words().size() <= 5) return SPV_SUCCESS; - - const uint32_t mask = inst->word(5); if (spv_result_t result = - ValidateImageOperands(_, inst, info, mask, /* word_index = */ 6)) + ValidateImageOperands(_, inst, info, /* word_index = */ 6)) return result; return SPV_SUCCESS; @@ -1336,6 +1417,14 @@ spv_result_t ValidateImageGather(ValidationState_t& _, << "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 == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather || _.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) { const uint32_t result_component_type = @@ -1368,12 +1457,21 @@ spv_result_t ValidateImageGather(ValidationState_t& _, } if (opcode == SpvOpImageGather || opcode == SpvOpImageSparseGather) { - const uint32_t component_index_type = _.GetOperandTypeId(inst, 4); + const uint32_t component = inst->GetOperandAs<uint32_t>(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 == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather); @@ -1384,11 +1482,8 @@ spv_result_t ValidateImageGather(ValidationState_t& _, } } - if (inst->words().size() <= 6) return SPV_SUCCESS; - - const uint32_t mask = inst->word(6); if (spv_result_t result = - ValidateImageOperands(_, inst, info, mask, /* word_index = */ 7)) + ValidateImageOperands(_, inst, info, /* word_index = */ 7)) return result; return SPV_SUCCESS; @@ -1408,14 +1503,16 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { << " to be int or float scalar or vector type"; } -#if 0 - // TODO(atgoo@github.com) Disabled until the spec is clarified. - if (_.GetDimension(actual_result_type) != 4) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected " << GetActualResultTypeStr(opcode) - << " to have 4 components"; - } -#endif + 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) + << "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) != SpvOpTypeImage) { @@ -1429,6 +1526,29 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* 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"; + } + } + } + if (info.dim == SpvDimSubpassData) { if (opcode == SpvOpImageSparseRead) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -1477,12 +1597,10 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { } } - if (inst->words().size() <= 5) return SPV_SUCCESS; - - const uint32_t mask = inst->word(5); + const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); - if (spvIsOpenCLEnv(_.context()->target_env)) { - if (mask & SpvImageOperandsConstOffsetMask) { + if (mask & SpvImageOperandsConstOffsetMask) { + if (spvIsOpenCLEnv(_.context()->target_env)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "ConstOffset image operand not allowed " << "in the OpenCL environment."; @@ -1490,7 +1608,7 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { } if (spv_result_t result = - ValidateImageOperands(_, inst, info, mask, /* word_index = */ 6)) + ValidateImageOperands(_, inst, info, /* word_index = */ 6)) return result; return SPV_SUCCESS; @@ -1566,9 +1684,7 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { } } - if (inst->words().size() <= 4) { - return SPV_SUCCESS; - } else { + 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 " @@ -1576,9 +1692,8 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { } } - const uint32_t mask = inst->word(4); if (spv_result_t result = - ValidateImageOperands(_, inst, info, mask, /* word_index = */ 5)) + ValidateImageOperands(_, inst, info, /* word_index = */ 5)) return result; return SPV_SUCCESS; @@ -1649,6 +1764,16 @@ spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _, 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) @@ -1823,6 +1948,13 @@ spv_result_t ValidateImageQueryLod(ValidationState_t& _, << "Expected Coordinate to have at least " << min_coord_size << " components, but given only " << actual_coord_size; } + + // The operad 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; } @@ -1859,6 +1991,15 @@ spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _, 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 == SpvOpImageQuerySamples); if (info.dim != SpvDim2D) { diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index d9f8b991..4dd6d941 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -407,6 +407,10 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { << "' is not a pointer type."; } + const auto type_index = 2; + const auto value_id = result_type->GetOperandAs<uint32_t>(type_index); + auto value_type = _.FindDef(value_id); + const auto initializer_index = 3; const auto storage_class_index = 2; if (initializer_index < inst->operands().size()) { @@ -423,7 +427,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { << "OpVariable Initializer <id> '" << _.getIdName(initializer_id) << "' is not a constant or module-scope variable."; } - if (initializer->type_id() != result_type->GetOperandAs<uint32_t>(2u)) { + if (initializer->type_id() != value_id) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Initializer type must match the type pointed to by the Result " "Type"; @@ -440,9 +444,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class != SpvStorageClassHitAttributeNV && storage_class != SpvStorageClassCallableDataNV && storage_class != SpvStorageClassIncomingCallableDataNV) { - const auto storage_index = 2; - const auto storage_id = result_type->GetOperandAs<uint32_t>(storage_index); - const auto storage = _.FindDef(storage_id); bool storage_input_or_output = storage_class == SpvStorageClassInput || storage_class == SpvStorageClassOutput; bool builtin = false; @@ -455,7 +456,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } if (!(storage_input_or_output && builtin) && - ContainsInvalidBool(_, storage, storage_input_or_output)) { + ContainsInvalidBool(_, value_type, storage_input_or_output)) { 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 " @@ -535,18 +536,14 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (!IsAllowedTypeOrArrayOfSame( _, pointee, {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage, - SpvOpTypeAccelerationStructureNV, - SpvOpTypeAccelerationStructureKHR, SpvOpTypeRayQueryKHR})) { + SpvOpTypeAccelerationStructureKHR})) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "UniformConstant OpVariable <id> '" << _.getIdName(inst->id()) - << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.2:\n" + << _.VkErrorID(4655) << "UniformConstant OpVariable <id> '" + << _.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, OpTypeAccelerationStructureNV, " - "OpTypeAccelerationStructureKHR, " - "OpTypeRayQueryKHR, " + << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " << "or an array of one of these types."; } } @@ -576,40 +573,59 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { "of this type"; } } + + // Check for invalid use of Invariant + if (storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + if (_.HasDecoration(inst->id(), SpvDecorationInvariant)) { + 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() == SpvOpTypeStruct) { + if (_.HasDecoration(value_id, SpvDecorationInvariant)) { + 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."; + } + } + } } - // WebGPU & Vulkan Appendix A: Check that if contains initializer, then + // Vulkan Appendix A: Check that if contains initializer, then // storage class is Output, Private, or Function. if (inst->operands().size() > 3 && storage_class != SpvStorageClassOutput && storage_class != SpvStorageClassPrivate && storage_class != SpvStorageClassFunction) { - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable, <id> '" << _.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, or " - << "Function"; + if (spvIsVulkanEnv(_.context()->target_env)) { + if (storage_class == SpvStorageClassWorkgroup) { + auto init_id = inst->GetOperandAs<uint32_t>(3); + auto init = _.FindDef(init_id); + if (init->opcode() != SpvOpConstantNull) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Variable initializers in Workgroup storage class are " + "limited to OpConstantNull"; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4651) << "OpVariable, <id> '" + << _.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"; + } } } - // WebGPU: All variables with storage class Output, Private, or Function MUST - // have an initializer. - if (spvIsWebGPUEnv(_.context()->target_env) && inst->operands().size() <= 3 && - (storage_class == SpvStorageClassOutput || - storage_class == SpvStorageClassPrivate || - storage_class == SpvStorageClassFunction)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable, <id> '" << _.getIdName(inst->id()) - << "', must have an initializer.\n" - << "From WebGPU execution environment spec:\n" - << "All variables in the following storage classes must have an " - << "initializer: Output, Private, or Function"; - } - if (storage_class == SpvStorageClassPhysicalStorageBufferEXT) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "PhysicalStorageBufferEXT must not be used with OpVariable."; @@ -643,9 +659,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } // Vulkan specific validation rules for OpTypeRuntimeArray - const auto type_index = 2; - const auto value_id = result_type->GetOperandAs<uint32_t>(type_index); - auto value_type = _.FindDef(value_id); if (spvIsVulkanEnv(_.context()->target_env)) { // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct, // so should never appear as a bare variable. @@ -702,41 +715,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } - // WebGPU specific validation rules for OpTypeRuntimeArray - if (spvIsWebGPUEnv(_.context()->target_env)) { - // OpTypeRuntimeArray should only ever be in an OpTypeStruct, - // so should never appear as a bare variable. - if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable, <id> '" << _.getIdName(inst->id()) - << "', is attempting to create memory for an illegal type, " - << "OpTypeRuntimeArray.\nFor WebGPU OpTypeRuntimeArray can only " - << "appear as the final member of an OpTypeStruct, thus cannot " - << "be instantiated via OpVariable"; - } - - // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it - // must have the storage class StorageBuffer and be decorated - // with Block. - if (value_type && value_type->opcode() == SpvOpTypeStruct) { - if (DoesStructContainRTA(_, value_type)) { - if (storage_class == SpvStorageClassStorageBuffer) { - if (!_.HasDecoration(value_id, SpvDecorationBlock)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "For WebGPU, an OpTypeStruct variable containing an " - << "OpTypeRuntimeArray must be decorated with Block if it " - << "has storage class StorageBuffer."; - } - } else { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "For WebGPU, OpTypeStruct variables containing " - << "OpTypeRuntimeArray must have storage class of " - << "StorageBuffer"; - } - } - } - } - // Cooperative matrix types can only be allocated in Function or Private if ((storage_class != SpvStorageClassFunction && storage_class != SpvStorageClassPrivate) && @@ -797,6 +775,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class_ok = false; } break; + case SpvStorageClassWorkgroup: + if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR)) { + storage_class_ok = false; + } + break; default: return _.diag(SPV_ERROR_INVALID_ID, inst) << "Cannot allocate a variable containing a 16-bit type in " @@ -848,6 +831,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class_ok = false; } break; + case SpvStorageClassWorkgroup: + if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR)) { + storage_class_ok = false; + } + break; default: return _.diag(SPV_ERROR_INVALID_ID, inst) << "Cannot allocate a variable containing a 8-bit type in " diff --git a/source/val/validate_memory_semantics.cpp b/source/val/validate_memory_semantics.cpp index 4c582f09..6c3e1d44 100644 --- a/source/val/validate_memory_semantics.cpp +++ b/source/val/validate_memory_semantics.cpp @@ -56,55 +56,6 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, return SPV_SUCCESS; } - if (spvIsWebGPUEnv(_.context()->target_env)) { - uint32_t valid_bits; - switch (inst->opcode()) { - case SpvOpControlBarrier: - if (!(value & SpvMemorySemanticsAcquireReleaseMask)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For WebGPU, AcquireRelease must be set for Memory " - "Semantics of OpControlBarrier."; - } - - if (!(value & SpvMemorySemanticsWorkgroupMemoryMask)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For WebGPU, WorkgroupMemory must be set for Memory " - "Semantics of OpControlBarrier."; - } - - valid_bits = SpvMemorySemanticsAcquireReleaseMask | - SpvMemorySemanticsWorkgroupMemoryMask; - if (value & ~valid_bits) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For WebGPU only WorkgroupMemory and AcquireRelease may be " - "set for Memory Semantics of OpControlBarrier."; - } - break; - case SpvOpMemoryBarrier: - if (!(value & SpvMemorySemanticsImageMemoryMask)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For WebGPU, ImageMemory must be set for Memory Semantics " - "of OpMemoryBarrier."; - } - valid_bits = SpvMemorySemanticsImageMemoryMask; - if (value & ~valid_bits) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For WebGPU only ImageMemory may be set for Memory " - "Semantics of OpMemoryBarrier."; - } - break; - default: - if (spvOpcodeIsAtomicOp(inst->opcode())) { - if (value != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "For WebGPU Memory no bits may be set for Memory " - "Semantics of OpAtomic* instructions."; - } - } - break; - } - } - const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits( value & (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask | SpvMemorySemanticsAcquireReleaseMask | @@ -221,7 +172,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, if (opcode == SpvOpMemoryBarrier && !num_memory_order_set_bits) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) + << _.VkErrorID(4732) << spvOpcodeString(opcode) << ": Vulkan specification requires Memory Semantics to have " "one " "of the following bits set: Acquire, Release, " @@ -231,7 +182,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, if (opcode == SpvOpMemoryBarrier && !includes_storage_class) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) + << _.VkErrorID(4733) << spvOpcodeString(opcode) << ": expected Memory Semantics to include a Vulkan-supported " "storage class"; } @@ -272,6 +223,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, value & SpvMemorySemanticsAcquireReleaseMask || value & SpvMemorySemanticsSequentiallyConsistentMask)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4731) << "Vulkan spec disallows OpAtomicLoad with Memory Semantics " "Release, AcquireRelease and SequentiallyConsistent"; } @@ -281,6 +233,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, value & SpvMemorySemanticsAcquireReleaseMask || value & SpvMemorySemanticsSequentiallyConsistentMask)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4730) << "Vulkan spec disallows OpAtomicStore with Memory Semantics " "Acquire, AcquireRelease and SequentiallyConsistent"; } diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp index 8cccd4c5..0c30f3ca 100644 --- a/source/val/validate_misc.cpp +++ b/source/val/validate_misc.cpp @@ -37,10 +37,6 @@ spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) { << "Cannot create undefined values with 8- or 16-bit types"; } - if (spvIsWebGPUEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_BINARY, inst) << "OpUndef is disallowed"; - } - return SPV_SUCCESS; } @@ -56,7 +52,7 @@ spv_result_t ValidateShaderClock(ValidationState_t& _, std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope); if (is_const_int32 && value != SpvScopeSubgroup && value != SpvScopeDevice) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Scope must be Subgroup or Device"; + << _.VkErrorID(4652) << "Scope must be Subgroup or Device"; } // Result Type must be a 64 - bit unsigned integer type or diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp index a7f8d339..79f82d8d 100644 --- a/source/val/validate_mode_setting.cpp +++ b/source/val/validate_mode_setting.cpp @@ -42,7 +42,8 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { 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) - << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id) + << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> '" + << _.getIdName(entry_point_id) << "'s function parameter count is not zero."; } } @@ -50,7 +51,8 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { auto return_type = _.FindDef(entry_point->type_id()); if (!return_type || SpvOpTypeVoid != return_type->opcode()) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id) + << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> '" + << _.getIdName(entry_point_id) << "'s function return type is not void."; } @@ -226,6 +228,7 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { } if (!ok) { return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4683) << "In the Vulkan environment, GLCompute execution model " "entry points require either the LocalSize execution " "mode or an object decorated with WorkgroupSize must be " @@ -457,31 +460,18 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _, if (spvIsVulkanEnv(_.context()->target_env)) { if (mode == SpvExecutionModeOriginLowerLeft) { return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4653) << "In the Vulkan environment, the OriginLowerLeft execution mode " "must not be used."; } if (mode == SpvExecutionModePixelCenterInteger) { return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4654) << "In the Vulkan environment, the PixelCenterInteger execution " "mode must not be used."; } } - if (spvIsWebGPUEnv(_.context()->target_env)) { - if (mode != SpvExecutionModeOriginUpperLeft && - mode != SpvExecutionModeDepthReplacing && - mode != SpvExecutionModeDepthGreater && - mode != SpvExecutionModeDepthLess && - mode != SpvExecutionModeDepthUnchanged && - mode != SpvExecutionModeLocalSize && - mode != SpvExecutionModeLocalSizeHint) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Execution mode must be one of OriginUpperLeft, " - "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, " - "LocalSize, or LocalSizeHint for WebGPU environment."; - } - } - return SPV_SUCCESS; } @@ -496,13 +486,6 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _, "the VulkanKHR memory model is used."; } - if (spvIsWebGPUEnv(_.context()->target_env)) { - if (_.addressing_model() != SpvAddressingModelLogical) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Addressing model must be Logical for WebGPU environment."; - } - } - if (spvIsOpenCLEnv(_.context()->target_env)) { if ((_.addressing_model() != SpvAddressingModelPhysical32) && (_.addressing_model() != SpvAddressingModelPhysical64)) { @@ -516,6 +499,15 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _, } } + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((_.addressing_model() != SpvAddressingModelLogical) && + (_.addressing_model() != SpvAddressingModelPhysicalStorageBuffer64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4635) + << "Addressing model must be Logical or PhysicalStorageBuffer64 " + << "in the Vulkan environment."; + } + } return SPV_SUCCESS; } diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp index 8dcf9743..2b6eb8b5 100644 --- a/source/val/validate_non_uniform.cpp +++ b/source/val/validate_non_uniform.cpp @@ -47,6 +47,19 @@ spv_result_t ValidateGroupNonUniformBallotBitCount(ValidationState_t& _, "vector of four components " "of integer type scalar"; } + + const auto group = inst->GetOperandAs<uint32_t>(3); + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((group != SpvGroupOperationReduce) && + (group != SpvGroupOperationInclusiveScan) && + (group != SpvGroupOperationExclusiveScan)) { + 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; } diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index a6fb26da..a92f7fd3 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp @@ -99,7 +99,7 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _, if (spvOpcodeIsNonUniformGroupOperation(opcode) && value != SpvScopeSubgroup) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) + << _.VkErrorID(4642) << spvOpcodeString(opcode) << ": in Vulkan environment Execution scope is limited to " << "Subgroup"; } @@ -137,30 +137,6 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _, } } - // WebGPU Specific rules - if (spvIsWebGPUEnv(_.context()->target_env)) { - if (value != SpvScopeWorkgroup) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": in WebGPU environment Execution Scope is limited to " - << "Workgroup"; - } else { - _.function(inst->function()->id()) - ->RegisterExecutionModelLimitation( - [](SpvExecutionModel model, std::string* message) { - if (model != SpvExecutionModelGLCompute) { - if (message) { - *message = - ": in WebGPU environment, Workgroup Execution Scope is " - "limited to GLCompute execution model"; - } - return false; - } - return true; - }); - } - } - // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. // General SPIRV rules @@ -214,7 +190,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, if (spvIsVulkanEnv(_.context()->target_env)) { if (value == SpvScopeCrossDevice) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) + << _.VkErrorID(4638) << spvOpcodeString(opcode) << ": in Vulkan environment, Memory Scope cannot be CrossDevice"; } // Vulkan 1.0 specifc rules @@ -222,7 +198,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, value != SpvScopeDevice && value != SpvScopeWorkgroup && value != SpvScopeInvocation) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) + << _.VkErrorID(4638) << spvOpcodeString(opcode) << ": in Vulkan 1.0 environment Memory Scope is limited to " << "Device, Workgroup and Invocation"; } @@ -233,15 +209,16 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, value != SpvScopeSubgroup && value != SpvScopeInvocation && value != SpvScopeShaderCallKHR) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) + << _.VkErrorID(4638) << spvOpcodeString(opcode) << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited " << "to Device, Workgroup, Invocation, and ShaderCall"; } if (value == SpvScopeShaderCallKHR) { + std::string errorVUID = _.VkErrorID(4640); _.function(inst->function()->id()) ->RegisterExecutionModelLimitation( - [](SpvExecutionModel model, std::string* message) { + [errorVUID](SpvExecutionModel model, std::string* message) { if (model != SpvExecutionModelRayGenerationKHR && model != SpvExecutionModelIntersectionKHR && model != SpvExecutionModelAnyHitKHR && @@ -250,6 +227,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, model != SpvExecutionModelCallableKHR) { if (message) { *message = + errorVUID + "ShaderCallKHR Memory Scope requires a ray tracing " "execution model"; } @@ -258,56 +236,19 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, return true; }); } - } - - // WebGPU specific rules - if (spvIsWebGPUEnv(_.context()->target_env)) { - switch (inst->opcode()) { - case SpvOpControlBarrier: - if (value != SpvScopeWorkgroup) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": in WebGPU environment Memory Scope is limited to " - << "Workgroup for OpControlBarrier"; - } - break; - case SpvOpMemoryBarrier: - if (value != SpvScopeWorkgroup) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": in WebGPU environment Memory Scope is limited to " - << "Workgroup for OpMemoryBarrier"; - } - break; - default: - if (spvOpcodeIsAtomicOp(inst->opcode())) { - if (value != SpvScopeQueueFamilyKHR) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": in WebGPU environment Memory Scope is limited to " - << "QueueFamilyKHR for OpAtomic* operations"; - } - } - - if (value != SpvScopeWorkgroup && value != SpvScopeInvocation && - value != SpvScopeQueueFamilyKHR) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": in WebGPU environment Memory Scope is limited to " - << "Workgroup, Invocation, and QueueFamilyKHR"; - } - break; - } if (value == SpvScopeWorkgroup) { + std::string errorVUID = _.VkErrorID(4639); _.function(inst->function()->id()) ->RegisterExecutionModelLimitation( - [](SpvExecutionModel model, std::string* message) { - if (model != SpvExecutionModelGLCompute) { + [errorVUID](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelGLCompute && + model != SpvExecutionModelTaskNV && + model != SpvExecutionModelMeshNV) { if (message) { - *message = - ": in WebGPU environment, Workgroup Memory Scope is " - "limited to GLCompute execution model"; + *message = errorVUID + + "Workgroup Memory Scope is limited to MeshNV, " + "TaskNV, and GLCompute execution model"; } return false; } diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp index 5924c69f..6a5ea3c1 100644 --- a/source/val/validate_type.cpp +++ b/source/val/validate_type.cpp @@ -40,21 +40,6 @@ int64_t ConstantLiteralAsInt64(uint32_t width, return static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32); } -// Returns, as an uint64_t, the literal value from an OpConstant or the -// default value of an OpSpecConstant, assuming it is an integral type. -// For signed integers, relies the rule that literal value is sign extended -// to fill out to word granularity. Assumes that the constant value -// has -int64_t ConstantLiteralAsUint64(uint32_t width, - const std::vector<uint32_t>& const_words) { - const uint32_t lo_word = const_words[3]; - if (width <= 32) return lo_word; - assert(width <= 64); - assert(const_words.size() > 4); - const uint32_t hi_word = const_words[4]; // Must exist, per spec. - return (uint64_t(lo_word) | uint64_t(hi_word) << 32); -} - // 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) @@ -240,7 +225,7 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { << "' is a void type."; } - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) && + if (spvIsVulkanEnv(_.context()->target_env) && element_type->opcode() == SpvOpTypeRuntimeArray) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id) @@ -279,18 +264,6 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { << "OpTypeArray Length <id> '" << _.getIdName(length_id) << "' default value must be at least 1: found " << ivalue; } - if (spvIsWebGPUEnv(_.context()->target_env)) { - // WebGPU has maximum integer width of 32 bits, and max array size - // is one more than the max signed integer representation. - const uint64_t max_permitted = (uint64_t(1) << 31); - const uint64_t uvalue = ConstantLiteralAsUint64(width, length->words()); - if (uvalue > max_permitted) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Length <id> '" << _.getIdName(length_id) - << "' size exceeds max value " << max_permitted - << " permitted by WebGPU: got " << uvalue; - } - } } break; case SpvOpConstantNull: return _.diag(SPV_ERROR_INVALID_ID, inst) @@ -322,7 +295,7 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _, << _.getIdName(element_id) << "' is a void type."; } - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) && + if (spvIsVulkanEnv(_.context()->target_env) && element_type->opcode() == SpvOpTypeRuntimeArray) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpTypeRuntimeArray Element Type <id> '" @@ -394,7 +367,7 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { << "."; } - if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) && + if (spvIsVulkanEnv(_.context()->target_env) && member_type->opcode() == SpvOpTypeRuntimeArray) { const bool is_last_member = member_type_index == inst->operands().size() - 1; @@ -555,8 +528,8 @@ spv_result_t ValidateTypeForwardPointer(ValidationState_t& _, << "Pointer type in OpTypeForwardPointer is not a pointer type."; } - if (inst->GetOperandAs<uint32_t>(1) != - pointer_type_inst->GetOperandAs<uint32_t>(1)) { + const auto storage_class = inst->GetOperandAs<SpvStorageClass>(1); + if (storage_class != pointer_type_inst->GetOperandAs<uint32_t>(1)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Storage class in OpTypeForwardPointer does not match the " << "pointer definition."; @@ -569,6 +542,15 @@ spv_result_t ValidateTypeForwardPointer(ValidationState_t& _, << "Forward pointers must point to a structure"; } + if (spvIsVulkanEnv(_.context()->target_env)) { + if (storage_class != SpvStorageClassPhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4711) + << "In Vulkan, OpTypeForwardPointer must have " + << "a storage class of PhysicalStorageBuffer."; + } + } + return SPV_SUCCESS; } diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 06abb54f..6dfc7bf6 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -359,6 +359,7 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) { case SpvCapabilityStorageBuffer8BitAccess: case SpvCapabilityUniformAndStorageBuffer8BitAccess: case SpvCapabilityStoragePushConstant8: + case SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR: features_.declare_int8_type = true; break; case SpvCapabilityInt16: @@ -372,6 +373,7 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) { case SpvCapabilityStorageUniform16: case SpvCapabilityStoragePushConstant16: case SpvCapabilityStorageInputOutput16: + case SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR: features_.declare_int16_type = true; features_.declare_float16_type = true; features_.free_fp_rounding_mode = true; @@ -1240,23 +1242,6 @@ bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { bool ValidationState_t::IsValidStorageClass( SpvStorageClass storage_class) const { - if (spvIsWebGPUEnv(context()->target_env)) { - switch (storage_class) { - case SpvStorageClassUniformConstant: - case SpvStorageClassUniform: - case SpvStorageClassStorageBuffer: - case SpvStorageClassInput: - case SpvStorageClassOutput: - case SpvStorageClassImage: - case SpvStorageClassWorkgroup: - case SpvStorageClassPrivate: - case SpvStorageClassFunction: - return true; - default: - return false; - } - } - if (spvIsVulkanEnv(context()->target_env)) { switch (storage_class) { case SpvStorageClassUniformConstant: @@ -1315,10 +1300,22 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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 4205: @@ -1345,12 +1342,36 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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: @@ -1419,6 +1440,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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: @@ -1431,6 +1454,12 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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: @@ -1477,6 +1506,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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: @@ -1523,6 +1554,40 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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: @@ -1531,10 +1596,18 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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: @@ -1551,6 +1624,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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: @@ -1599,6 +1674,66 @@ std::string ValidationState_t::VkErrorID(uint32_t id, 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 4635: + return VUID_WRAP(VUID-StandaloneSpirv-None-04635); + case 4638: + return VUID_WRAP(VUID-StandaloneSpirv-None-04638); + case 4639: + return VUID_WRAP(VUID-StandaloneSpirv-None-04639); + case 4640: + return VUID_WRAP(VUID-StandaloneSpirv-None-04640); + case 4642: + return VUID_WRAP(VUID-StandaloneSpirv-None-04642); + 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 4662: + return VUID_WRAP(VUID-StandaloneSpirv-Offset-04662); + case 4663: + return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663); + case 4664: + return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664); + case 4669: + return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669); + case 4675: + return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); + case 4677: + return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); + case 4683: + return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683); + case 4685: + return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); + case 4686: + return VUID_WRAP(VUID-StandaloneSpirv-None-04686); + 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); default: return ""; // unknown id }; diff --git a/source/val/validation_state.h b/source/val/validation_state.h index aeb1ca86..85111391 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -103,6 +103,10 @@ class ValidationState_t { // Members need not be listed in offset order bool scalar_block_layout = false; + // Use scalar block layout (as defined above) for Workgroup block + // variables. See VK_KHR_workgroup_memory_explicit_layout. + bool workgroup_scalar_block_layout = false; + // SPIR-V 1.4 allows us to select between any two composite values // of the same type. bool select_between_composites = false; diff --git a/test/fuzz/transformation_duplicate_region_with_selection_test.cpp b/test/fuzz/transformation_duplicate_region_with_selection_test.cpp index f3738e74..31fb9a2f 100644 --- a/test/fuzz/transformation_duplicate_region_with_selection_test.cpp +++ b/test/fuzz/transformation_duplicate_region_with_selection_test.cpp @@ -1334,6 +1334,9 @@ TEST(TransformationDuplicateRegionWithSelectionTest, OpBranch %50 %50 = OpLabel %51 = OpCopyObject %7 %12 + OpBranch %52 + %52 = OpLabel + %53 = OpCopyObject %7 %51 OpReturn OpFunctionEnd )"; @@ -2275,6 +2278,207 @@ TEST(TransformationDuplicateRegionWithSelectionTest, .IsApplicable(context.get(), transformation_context)); } +TEST(TransformationDuplicateRegionWithSelectionTest, + DoNotProduceOpPhiWithVoidType) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %5 + %5 = OpLabel + %8 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation( + 100, 11, 101, 5, 5, {{5, 102}}, {{8, 103}}, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %100 + %100 = OpLabel + OpSelectionMerge %101 None + OpBranchConditional %11 %5 %102 + %5 = OpLabel + %8 = OpFunctionCall %2 %6 + OpBranch %101 + %102 = OpLabel + %103 = OpFunctionCall %2 %6 + OpBranch %101 + %101 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + DoNotProduceOpPhiWithDisallowedType) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpTypeImage %6 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 + %12 = OpTypePointer UniformConstant %11 + %13 = OpVariable %12 UniformConstant + %15 = OpConstant %6 1 + %16 = OpConstantComposite %7 %15 %15 + %17 = OpTypeVector %6 4 + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 0 + %22 = OpTypePointer Function %6 + %90 = OpTypeBool + %91 = OpConstantTrue %90 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpBranch %81 + %81 = OpLabel + %14 = OpLoad %11 %13 + %18 = OpImageSampleImplicitLod %17 %14 %16 + %21 = OpCompositeExtract %6 %18 0 + %23 = OpAccessChain %22 %9 %20 + OpStore %23 %21 + OpBranch %80 + %80 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation( + 100, 91, 101, 81, 81, {{81, 102}}, + {{14, 103}, {18, 104}, {21, 105}, {23, 106}}, {{18, 107}, {21, 108}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpTypeImage %6 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 + %12 = OpTypePointer UniformConstant %11 + %13 = OpVariable %12 UniformConstant + %15 = OpConstant %6 1 + %16 = OpConstantComposite %7 %15 %15 + %17 = OpTypeVector %6 4 + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 0 + %22 = OpTypePointer Function %6 + %90 = OpTypeBool + %91 = OpConstantTrue %90 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpBranch %100 + %100 = OpLabel + OpSelectionMerge %101 None + OpBranchConditional %91 %81 %102 + %81 = OpLabel + %14 = OpLoad %11 %13 + %18 = OpImageSampleImplicitLod %17 %14 %16 + %21 = OpCompositeExtract %6 %18 0 + %23 = OpAccessChain %22 %9 %20 + OpStore %23 %21 + OpBranch %101 + %102 = OpLabel + %103 = OpLoad %11 %13 + %104 = OpImageSampleImplicitLod %17 %103 %16 + %105 = OpCompositeExtract %6 %104 0 + %106 = OpAccessChain %22 %9 %20 + OpStore %106 %105 + OpBranch %101 + %101 = OpLabel + %107 = OpPhi %17 %18 %81 %104 %102 + %108 = OpPhi %6 %21 %81 %105 %102 + OpBranch %80 + %80 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn index be1258a4..ec09b2b0 100644 --- a/test/fuzzers/BUILD.gn +++ b/test/fuzzers/BUILD.gn @@ -39,10 +39,7 @@ if (!build_with_chromium || use_fuzzing_engine) { ":spvtools_opt_legalization_fuzzer", ":spvtools_opt_performance_fuzzer", ":spvtools_opt_size_fuzzer", - ":spvtools_opt_webgputovulkan_fuzzer", - ":spvtools_opt_vulkantowebgpu_fuzzer", ":spvtools_val_fuzzer", - ":spvtools_val_webgpu_fuzzer", ] } } @@ -104,31 +101,12 @@ spvtools_fuzzer("spvtools_opt_size_fuzzer_src") { ] } - -spvtools_fuzzer("spvtools_opt_webgputovulkan_fuzzer_src") { - sources = [ - "spvtools_opt_webgputovulkan_fuzzer.cpp", - ] -} - -spvtools_fuzzer("spvtools_opt_vulkantowebgpu_fuzzer_src") { - sources = [ - "spvtools_opt_vulkantowebgpu_fuzzer.cpp", - ] -} - spvtools_fuzzer("spvtools_val_fuzzer_src") { sources = [ "spvtools_val_fuzzer.cpp", ] } -spvtools_fuzzer("spvtools_val_webgpu_fuzzer_src") { - sources = [ - "spvtools_val_webgpu_fuzzer.cpp", - ] -} - if (!build_with_chromium || use_fuzzing_engine) { fuzzer_test("spvtools_as_fuzzer") { sources = [] @@ -181,22 +159,6 @@ if (!build_with_chromium || use_fuzzing_engine) { seed_corpus = "corpora/spv" } - fuzzer_test("spvtools_opt_webgputovulkan_fuzzer") { - sources = [] - deps = [ - ":spvtools_opt_webgputovulkan_fuzzer_src", - ] - seed_corpus = "corpora/spv" - } - - fuzzer_test("spvtools_opt_vulkantowebgpu_fuzzer") { - sources = [] - deps = [ - ":spvtools_opt_vulkantowebgpu_fuzzer_src", - ] - seed_corpus = "corpora/spv" - } - fuzzer_test("spvtools_val_fuzzer") { sources = [] deps = [ @@ -204,12 +166,4 @@ if (!build_with_chromium || use_fuzzing_engine) { ] seed_corpus = "corpora/spv" } - - fuzzer_test("spvtools_val_webgpu_fuzzer") { - sources = [] - deps = [ - ":spvtools_val_webgpu_fuzzer_src", - ] - seed_corpus = "corpora/spv" - } } diff --git a/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp b/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp deleted file mode 100644 index 9371c0df..00000000 --- a/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// 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. - -#include <cstdint> -#include <vector> - -#include "spirv-tools/optimizer.hpp" - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector<uint32_t> input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterVulkanToWebGPUPasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; -} diff --git a/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp b/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp deleted file mode 100644 index 78ddbb75..00000000 --- a/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// 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. - -#include <cstdint> -#include <vector> - -#include "spirv-tools/optimizer.hpp" - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector<uint32_t> input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterWebGPUToVulkanPasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; -} diff --git a/test/fuzzers/spvtools_val_webgpu_fuzzer.cpp b/test/fuzzers/spvtools_val_webgpu_fuzzer.cpp deleted file mode 100644 index bed6e1a2..00000000 --- a/test/fuzzers/spvtools_val_webgpu_fuzzer.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// 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. - -#include <cstdint> -#include <vector> - -#include "spirv-tools/libspirv.hpp" - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - spvtools::SpirvTools tools(SPV_ENV_WEBGPU_0); - tools.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector<uint32_t> input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - tools.Validate(input); - return 0; -} diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 34269587..79cb3fc4 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -34,7 +34,6 @@ add_spvtools_unittest(TARGET opt dead_insert_elim_test.cpp dead_variable_elim_test.cpp debug_info_manager_test.cpp - decompose_initialized_variables_test.cpp decoration_manager_test.cpp def_use_test.cpp desc_sroa_test.cpp @@ -48,7 +47,6 @@ add_spvtools_unittest(TARGET opt fold_test.cpp freeze_spec_const_test.cpp function_test.cpp - generate_webgpu_initializers_test.cpp graphics_robust_access_test.cpp if_conversion_test.cpp inline_opaque_test.cpp @@ -63,7 +61,6 @@ add_spvtools_unittest(TARGET opt ir_context_test.cpp ir_loader_test.cpp iterator_test.cpp - legalize_vector_shuffle_test.cpp line_debug_info_test.cpp local_access_chain_convert_test.cpp local_redundancy_elimination_test.cpp @@ -88,9 +85,7 @@ add_spvtools_unittest(TARGET opt scalar_replacement_test.cpp set_spec_const_default_value_test.cpp simplification_test.cpp - split_invalid_unreachable_test.cpp strength_reduction_test.cpp - strip_atomic_counter_memory_test.cpp strip_debug_info_test.cpp strip_reflect_info_test.cpp struct_cfg_analysis_test.cpp diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp index c1381547..ca6ee583 100644 --- a/test/opt/convert_relaxed_to_half_test.cpp +++ b/test/opt/convert_relaxed_to_half_test.cpp @@ -1331,6 +1331,72 @@ OpFunctionEnd SinglePassRunAndMatch<ConvertToHalfPass>(defs + func, true); } +TEST_F(ConvertToHalfTest, RemoveRelaxDec) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4117 + + // This test is a case where the relax precision decorations need to be + // removed, but the body of the function does not change because there are not + // arithmetic operations. So, there is not need for the Float16 capability. + const std::string test = + R"( +; CHECK-NOT: OpCapability Float16 +; GLSL seems to generate this decoration on the load of a texture, which seems odd to me. +; This pass does not currently remove it, and I'm not sure what we should do with it, so I will leave it. +; CHECK: OpDecorate [[tex:%\w+]] RelaxedPrecision +; CHECK-NOT: OpDecorate {{%\w+}} RelaxedPrecision +; CHECK: OpLabel +; CHECK: [[tex]] = OpLoad {{%\w+}} %sTexture +; CHECK: [[coord:%\w+]] = OpLoad %v2float +; CHECK: [[retval:%\w+]] = OpImageSampleImplicitLod %v4float {{%\w+}} [[coord]] +; CHECK: OpStore %outFragColor [[retval]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outFragColor %v_texcoord + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %outFragColor "outFragColor" + OpName %sTexture "sTexture" + OpName %v_texcoord "v_texcoord" + OpDecorate %outFragColor RelaxedPrecision + OpDecorate %outFragColor Location 0 + OpDecorate %sTexture RelaxedPrecision + OpDecorate %sTexture DescriptorSet 0 + OpDecorate %sTexture Binding 0 + OpDecorate %14 RelaxedPrecision + OpDecorate %v_texcoord RelaxedPrecision + OpDecorate %v_texcoord Location 0 + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outFragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %sTexture = OpVariable %_ptr_UniformConstant_11 UniformConstant + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v_texcoord = OpVariable %_ptr_Input_v2float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpLoad %11 %sTexture + %18 = OpLoad %v2float %v_texcoord + %19 = OpImageSampleImplicitLod %v4float %14 %18 + OpStore %outFragColor %19 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndMatch<ConvertToHalfPass>(test, true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp index 911331a7..49407fd5 100644 --- a/test/opt/debug_info_manager_test.cpp +++ b/test/opt/debug_info_manager_test.cpp @@ -31,6 +31,9 @@ static const uint32_t kDebugFunctionOperandFunctionIndex = 13; static const uint32_t kDebugInlinedAtOperandLineIndex = 4; static const uint32_t kDebugInlinedAtOperandScopeIndex = 5; static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6; +static const uint32_t kOpLineInOperandFileIndex = 0; +static const uint32_t kOpLineInOperandLineIndex = 1; +static const uint32_t kOpLineInOperandColumnIndex = 2; namespace spvtools { namespace opt { @@ -609,6 +612,80 @@ void main(float in_var_color : COLOR) { EXPECT_FALSE(dbg_info_mgr->IsVariableDebugDeclared(100)); } +TEST(DebugInfoManager, AddDebugValueForDecl) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + %14 = OpString "#line 1 \"ps.hlsl\" +void main(float in_var_color : COLOR) { + float color = in_var_color; +} +" + %17 = OpString "float" + %21 = OpString "main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main + %12 = OpExtInst %void %1 DebugInfoNone + %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0 + %main = OpFunction %void None %27 + %28 = OpLabel + %100 = OpVariable %_ptr_Function_float Function + %31 = OpLoad %float %in_var_COLOR + %101 = OpExtInst %void %1 DebugScope %22 + OpLine %5 13 7 + OpStore %100 %31 + OpNoLine + %102 = OpExtInst %void %1 DebugNoScope + %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto* def_use_mgr = context->get_def_use_mgr(); + auto* dbg_decl = def_use_mgr->GetDef(36); + EXPECT_EQ(dbg_decl->GetOpenCL100DebugOpcode(), + OpenCLDebugInfo100DebugDeclare); + + auto* dbg_info_mgr = context->get_debug_info_mgr(); + Instruction* store = dbg_decl->PreviousNode(); + auto* dbg_value = + dbg_info_mgr->AddDebugValueForDecl(dbg_decl, 100, dbg_decl, store); + + EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue); + EXPECT_EQ(dbg_value->dbg_line_inst()->GetSingleWordInOperand( + kOpLineInOperandFileIndex), + 5); + EXPECT_EQ(dbg_value->dbg_line_inst()->GetSingleWordInOperand( + kOpLineInOperandLineIndex), + 13); + EXPECT_EQ(dbg_value->dbg_line_inst()->GetSingleWordInOperand( + kOpLineInOperandColumnIndex), + 7); +} + } // namespace } // namespace analysis } // namespace opt diff --git a/test/opt/decompose_initialized_variables_test.cpp b/test/opt/decompose_initialized_variables_test.cpp deleted file mode 100644 index 06ba59a5..00000000 --- a/test/opt/decompose_initialized_variables_test.cpp +++ /dev/null @@ -1,252 +0,0 @@ -// 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 <vector> - -#include "test/opt/pass_fixture.h" -#include "test/opt/pass_utils.h" - -namespace spvtools { -namespace opt { -namespace { - -using DecomposeInitializedVariablesTest = PassTest<::testing::Test>; - -std::string single_entry_header = R"(OpCapability Shader -OpCapability VulkanMemoryModel -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical Vulkan -OpEntryPoint Vertex %1 "shader" -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%4 = OpConstantNull %uint -%void = OpTypeVoid -%6 = OpTypeFunction %void -)"; - -std::string GetFunctionTest(std::string body) { - auto result = single_entry_header; - result += "%_ptr_Function_uint = OpTypePointer Function %uint\n"; - result += "%1 = OpFunction %void None %6\n"; - result += "%8 = OpLabel\n"; - result += body + "\n"; - result += "OpReturn\n"; - result += "OpFunctionEnd\n"; - return result; -} - -TEST_F(DecomposeInitializedVariablesTest, FunctionChanged) { - std::string input = "%9 = OpVariable %_ptr_Function_uint Function %uint_1"; - std::string expected = R"(%9 = OpVariable %_ptr_Function_uint Function -OpStore %9 %uint_1)"; - - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - GetFunctionTest(input), GetFunctionTest(expected), - /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, FunctionUnchanged) { - std::string input = "%9 = OpVariable %_ptr_Function_uint Function"; - - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - GetFunctionTest(input), GetFunctionTest(input), /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, FunctionMultipleVariables) { - std::string input = R"(%9 = OpVariable %_ptr_Function_uint Function %uint_1 -%10 = OpVariable %_ptr_Function_uint Function %4)"; - std::string expected = R"(%9 = OpVariable %_ptr_Function_uint Function -%10 = OpVariable %_ptr_Function_uint Function -OpStore %9 %uint_1 -OpStore %10 %4)"; - - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - GetFunctionTest(input), GetFunctionTest(expected), - /* skip_nop = */ false); -} - -std::string GetGlobalTest(std::string storage_class, bool initialized, - bool decomposed) { - auto result = single_entry_header; - - result += "%_ptr_" + storage_class + "_uint = OpTypePointer " + - storage_class + " %uint\n"; - if (initialized) { - result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + - storage_class + " %4\n"; - } else { - result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + - storage_class + "\n"; - } - result += R"(%1 = OpFunction %void None %9 -%9 = OpLabel -)"; - if (decomposed) result += "OpStore %8 %4\n"; - result += R"(OpReturn -OpFunctionEnd -)"; - return result; -} - -TEST_F(DecomposeInitializedVariablesTest, PrivateChanged) { - std::string input = GetGlobalTest("Private", true, false); - std::string expected = GetGlobalTest("Private", false, true); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, expected, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, PrivateUnchanged) { - std::string input = GetGlobalTest("Private", false, false); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, input, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, OutputChanged) { - std::string input = GetGlobalTest("Output", true, false); - std::string expected = GetGlobalTest("Output", false, true); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, expected, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, OutputUnchanged) { - std::string input = GetGlobalTest("Output", false, false); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, input, /* skip_nop = */ false); -} - -std::string multiple_entry_header = R"(OpCapability Shader -OpCapability VulkanMemoryModel -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical Vulkan -OpEntryPoint Vertex %1 "vertex" -OpEntryPoint Fragment %2 "fragment" -%uint = OpTypeInt 32 0 -%4 = OpConstantNull %uint -%void = OpTypeVoid -%6 = OpTypeFunction %void -)"; - -std::string GetGlobalMultipleEntryTest(std::string storage_class, - bool initialized, bool decomposed) { - auto result = multiple_entry_header; - result += "%_ptr_" + storage_class + "_uint = OpTypePointer " + - storage_class + " %uint\n"; - if (initialized) { - result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + - storage_class + " %4\n"; - } else { - result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + - storage_class + "\n"; - } - result += R"(%1 = OpFunction %void None %9 -%9 = OpLabel -)"; - if (decomposed) result += "OpStore %8 %4\n"; - result += R"(OpReturn -OpFunctionEnd -%2 = OpFunction %void None %10 -%10 = OpLabel -)"; - if (decomposed) result += "OpStore %8 %4\n"; - result += R"(OpReturn -OpFunctionEnd -)"; - - return result; -} - -TEST_F(DecomposeInitializedVariablesTest, PrivateMultipleEntryChanged) { - std::string input = GetGlobalMultipleEntryTest("Private", true, false); - std::string expected = GetGlobalMultipleEntryTest("Private", false, true); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, expected, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, PrivateMultipleEntryUnchanged) { - std::string input = GetGlobalMultipleEntryTest("Private", false, false); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, input, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, OutputMultipleEntryChanged) { - std::string input = GetGlobalMultipleEntryTest("Output", true, false); - std::string expected = GetGlobalMultipleEntryTest("Output", false, true); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, expected, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, OutputMultipleEntryUnchanged) { - std::string input = GetGlobalMultipleEntryTest("Output", false, false); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, input, /* skip_nop = */ false); -} - -std::string GetGlobalWithNonEntryPointTest(std::string storage_class, - bool initialized, bool decomposed) { - auto result = single_entry_header; - result += "%_ptr_" + storage_class + "_uint = OpTypePointer " + - storage_class + " %uint\n"; - if (initialized) { - result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + - storage_class + " %4\n"; - } else { - result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + - storage_class + "\n"; - } - result += R"(%1 = OpFunction %void None %9 -%9 = OpLabel -)"; - if (decomposed) result += "OpStore %8 %4\n"; - result += R"(OpReturn -OpFunctionEnd -%10 = OpFunction %void None %11 -%11 = OpLabel -OpReturn -OpFunctionEnd -)"; - - return result; -} - -TEST_F(DecomposeInitializedVariablesTest, PrivateWithNonEntryPointChanged) { - std::string input = GetGlobalWithNonEntryPointTest("Private", true, false); - std::string expected = GetGlobalWithNonEntryPointTest("Private", false, true); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, expected, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, PrivateWithNonEntryPointUnchanged) { - std::string input = GetGlobalWithNonEntryPointTest("Private", false, false); - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, input, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, OutputWithNonEntryPointChanged) { - std::string input = GetGlobalWithNonEntryPointTest("Output", true, false); - std::string expected = GetGlobalWithNonEntryPointTest("Output", false, true); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, expected, /* skip_nop = */ false); -} - -TEST_F(DecomposeInitializedVariablesTest, OutputWithNonEntryPointUnchanged) { - std::string input = GetGlobalWithNonEntryPointTest("Output", false, false); - // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<DecomposeInitializedVariablesPass>( - input, input, /* skip_nop = */ false); -} - -} // namespace -} // namespace opt -} // namespace spvtools diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp index cdcc9a83..b35ad474 100644 --- a/test/opt/desc_sroa_test.cpp +++ b/test/opt/desc_sroa_test.cpp @@ -729,6 +729,47 @@ TEST_F(DescriptorScalarReplacementTest, ResourceStructAsFunctionParam) { SinglePassRunAndMatch<DescriptorScalarReplacement>(checks + shader, true); } +TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) { + // Check that correct binding numbers are given to an array of descriptors + // to structs. + + const std::string shader = R"( +; CHECK: OpDecorate {{%\w+}} Binding 0 +; CHECK: OpDecorate {{%\w+}} Binding 1 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "psmain" + OpExecutionMode %2 OriginUpperLeft + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpMemberDecorate %_struct_4 0 Offset 0 + OpMemberDecorate %_struct_4 1 Offset 4 + OpDecorate %_struct_4 Block + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %_struct_4 = OpTypeStruct %float %int +%_arr__struct_4_uint_2 = OpTypeArray %_struct_4 %uint_2 +%_ptr_Uniform__arr__struct_4_uint_2 = OpTypePointer Uniform %_arr__struct_4_uint_2 + %void = OpTypeVoid + %25 = OpTypeFunction %void +%_ptr_Uniform_int = OpTypePointer Uniform %int + %5 = OpVariable %_ptr_Uniform__arr__struct_4_uint_2 Uniform + %2 = OpFunction %void None %25 + %29 = OpLabel + %40 = OpAccessChain %_ptr_Uniform_int %5 %int_0 %int_1 + %41 = OpAccessChain %_ptr_Uniform_int %5 %int_1 %int_1 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/fix_storage_class_test.cpp b/test/opt/fix_storage_class_test.cpp index 4c8504ae..1c0101a0 100644 --- a/test/opt/fix_storage_class_test.cpp +++ b/test/opt/fix_storage_class_test.cpp @@ -528,6 +528,48 @@ TEST_F(FixStorageClassTest, FixLinkedAccessChain2) { SinglePassRunAndMatch<FixStorageClass>(text, false); } +TEST_F(FixStorageClassTest, AllowImageFormatMismatch) { + const std::string text = R"(OpCapability Shader +OpCapability SampledBuffer +OpCapability ImageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource HLSL 600 +OpName %type_buffer_image "type.buffer.image" +OpName %Buf "Buf" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %type_buffer_image_0 "type.buffer.image" +OpName %b "b" +OpDecorate %Buf DescriptorSet 0 +OpDecorate %Buf Binding 0 +%float = OpTypeFloat 32 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba16f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%void = OpTypeVoid +%11 = OpTypeFunction %void +%type_buffer_image_0 = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +%_ptr_Function_type_buffer_image_0 = OpTypePointer Function %type_buffer_image_0 +%Buf = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%main = OpFunction %void None %11 +%13 = OpLabel +%14 = OpFunctionCall %void %src_main +OpReturn +OpFunctionEnd +%src_main = OpFunction %void None %11 +%bb_entry = OpLabel +%b = OpVariable %_ptr_Function_type_buffer_image_0 Function +%15 = OpLoad %type_buffer_image %Buf +OpStore %b %15 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck<FixStorageClass>(text, text, false, false); +} + using FixTypeTest = PassTest<::testing::Test>; TEST_F(FixTypeTest, FixAccessChain) { diff --git a/test/opt/generate_webgpu_initializers_test.cpp b/test/opt/generate_webgpu_initializers_test.cpp deleted file mode 100644 index 4aab2ce2..00000000 --- a/test/opt/generate_webgpu_initializers_test.cpp +++ /dev/null @@ -1,347 +0,0 @@ -// 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 <vector> - -#include "test/opt/pass_fixture.h" -#include "test/opt/pass_utils.h" - -namespace spvtools { -namespace opt { -namespace { - -typedef std::tuple<std::string, bool> GenerateWebGPUInitializersParam; - -using GlobalVariableTest = - PassTest<::testing::TestWithParam<GenerateWebGPUInitializersParam>>; -using LocalVariableTest = - PassTest<::testing::TestWithParam<GenerateWebGPUInitializersParam>>; - -using GenerateWebGPUInitializersTest = PassTest<::testing::Test>; - -void operator+=(std::vector<const char*>& lhs, const char* rhs) { - lhs.push_back(rhs); -} - -void operator+=(std::vector<const char*>& lhs, - const std::vector<const char*>& rhs) { - lhs.reserve(lhs.size() + rhs.size()); - for (auto* c : rhs) lhs.push_back(c); -} - -std::string GetGlobalVariableTestString(std::string ptr_str, - std::string var_str, - std::string const_str = "") { - std::vector<const char*> result = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", - ptr_str.c_str()}; - // clang-format on - - if (!const_str.empty()) result += const_str.c_str(); - - result += { - // clang-format off - var_str.c_str(), - "%uint_0 = OpConstant %uint 0", - "%void = OpTypeVoid", - "%7 = OpTypeFunction %void", - "%1 = OpFunction %void None %7", - "%8 = OpLabel", - "OpStore %4 %uint_0", - "OpReturn", - "OpFunctionEnd" - // clang-format on - }; - return JoinAllInsts(result); -} - -std::string GetPointerString(std::string storage_type) { - std::string result = "%_ptr_"; - result += storage_type + "_uint = OpTypePointer "; - result += storage_type + " %uint"; - return result; -} - -std::string GetGlobalVariableString(std::string storage_type, - bool initialized) { - std::string result = "%4 = OpVariable %_ptr_"; - result += storage_type + "_uint "; - result += storage_type; - if (initialized) result += " %9"; - return result; -} - -std::string GetUninitializedGlobalVariableTestString(std::string storage_type) { - return GetGlobalVariableTestString( - GetPointerString(storage_type), - GetGlobalVariableString(storage_type, false)); -} - -std::string GetNullConstantString() { return "%9 = OpConstantNull %uint"; } - -std::string GetInitializedGlobalVariableTestString(std::string storage_type) { - return GetGlobalVariableTestString( - GetPointerString(storage_type), - GetGlobalVariableString(storage_type, true), GetNullConstantString()); -} - -TEST_P(GlobalVariableTest, Check) { - std::string storage_class = std::get<0>(GetParam()); - bool changed = std::get<1>(GetParam()); - std::string input = GetUninitializedGlobalVariableTestString(storage_class); - std::string expected = - changed ? GetInitializedGlobalVariableTestString(storage_class) : input; - - SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(input, expected, - /* skip_nop = */ false); -} - -// clang-format off -INSTANTIATE_TEST_SUITE_P( - GenerateWebGPUInitializers, GlobalVariableTest, - ::testing::ValuesIn(std::vector<GenerateWebGPUInitializersParam>({ - std::make_tuple("Private", true), - std::make_tuple("Output", true), - std::make_tuple("Function", true), - std::make_tuple("UniformConstant", false), - std::make_tuple("Input", false), - std::make_tuple("Uniform", false), - std::make_tuple("Workgroup", false) - }))); -// clang-format on - -std::string GetLocalVariableTestString(std::string ptr_str, std::string var_str, - std::string const_str = "") { - std::vector<const char*> result = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", - ptr_str.c_str(), - "%uint_0 = OpConstant %uint 0", - "%void = OpTypeVoid", - "%6 = OpTypeFunction %void"}; - // clang-format on - - if (!const_str.empty()) result += const_str.c_str(); - - result += { - // clang-format off - "%1 = OpFunction %void None %6", - "%7 = OpLabel", - var_str.c_str(), - "OpStore %8 %uint_0" - // clang-format on - }; - return JoinAllInsts(result); -} - -std::string GetLocalVariableString(std::string storage_type, bool initialized) { - std::string result = "%8 = OpVariable %_ptr_"; - result += storage_type + "_uint "; - result += storage_type; - if (initialized) result += " %9"; - return result; -} - -std::string GetUninitializedLocalVariableTestString(std::string storage_type) { - return GetLocalVariableTestString( - GetPointerString(storage_type), - GetLocalVariableString(storage_type, false)); -} - -std::string GetInitializedLocalVariableTestString(std::string storage_type) { - return GetLocalVariableTestString(GetPointerString(storage_type), - GetLocalVariableString(storage_type, true), - GetNullConstantString()); -} - -TEST_P(LocalVariableTest, Check) { - std::string storage_class = std::get<0>(GetParam()); - bool changed = std::get<1>(GetParam()); - - std::string input = GetUninitializedLocalVariableTestString(storage_class); - std::string expected = - changed ? GetInitializedLocalVariableTestString(storage_class) : input; - - SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(input, expected, - /* skip_nop = */ false); -} - -// clang-format off -INSTANTIATE_TEST_SUITE_P( - GenerateWebGPUInitializers, LocalVariableTest, - ::testing::ValuesIn(std::vector<GenerateWebGPUInitializersParam>({ - std::make_tuple("Private", true), - std::make_tuple("Output", true), - std::make_tuple("Function", true), - std::make_tuple("UniformConstant", false), - std::make_tuple("Input", false), - std::make_tuple("Uniform", false), - std::make_tuple("Workgroup", false) - }))); -// clang-format on - -TEST_F(GenerateWebGPUInitializersTest, AlreadyInitializedUnchanged) { - std::vector<const char*> spirv = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", - "%_ptr_Private_uint = OpTypePointer Private %uint", - "%uint_0 = OpConstant %uint 0", - "%5 = OpVariable %_ptr_Private_uint Private %uint_0", - "%void = OpTypeVoid", - "%7 = OpTypeFunction %void", - "%1 = OpFunction %void None %7", - "%8 = OpLabel", - "OpReturn", - "OpFunctionEnd" - // clang-format on - }; - std::string str = JoinAllInsts(spirv); - - SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(str, str, - /* skip_nop = */ false); -} - -TEST_F(GenerateWebGPUInitializersTest, AmbigiousArrays) { - std::vector<const char*> input_spirv = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", - "%uint_2 = OpConstant %uint 2", - "%_arr_uint_uint_2 = OpTypeArray %uint %uint_2", - "%_arr_uint_uint_2_0 = OpTypeArray %uint %uint_2", - "%_ptr_Private__arr_uint_uint_2 = OpTypePointer Private %_arr_uint_uint_2", -"%_ptr_Private__arr_uint_uint_2_0 = OpTypePointer Private %_arr_uint_uint_2_0", - "%8 = OpConstantNull %_arr_uint_uint_2_0", - "%9 = OpVariable %_ptr_Private__arr_uint_uint_2 Private", - "%10 = OpVariable %_ptr_Private__arr_uint_uint_2_0 Private %8", - "%void = OpTypeVoid", - "%12 = OpTypeFunction %void", - "%1 = OpFunction %void None %12", - "%13 = OpLabel", - "OpReturn", - "OpFunctionEnd" - // clang-format on - }; - std::string input_str = JoinAllInsts(input_spirv); - - std::vector<const char*> expected_spirv = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", - "%uint_2 = OpConstant %uint 2", - "%_arr_uint_uint_2 = OpTypeArray %uint %uint_2", - "%_arr_uint_uint_2_0 = OpTypeArray %uint %uint_2", - "%_ptr_Private__arr_uint_uint_2 = OpTypePointer Private %_arr_uint_uint_2", -"%_ptr_Private__arr_uint_uint_2_0 = OpTypePointer Private %_arr_uint_uint_2_0", - "%8 = OpConstantNull %_arr_uint_uint_2_0", - "%14 = OpConstantNull %_arr_uint_uint_2", - "%9 = OpVariable %_ptr_Private__arr_uint_uint_2 Private %14", - "%10 = OpVariable %_ptr_Private__arr_uint_uint_2_0 Private %8", - "%void = OpTypeVoid", - "%12 = OpTypeFunction %void", - "%1 = OpFunction %void None %12", - "%13 = OpLabel", - "OpReturn", - "OpFunctionEnd" - // clang-format on - }; - std::string expected_str = JoinAllInsts(expected_spirv); - - SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(input_str, expected_str, - /* skip_nop = */ false); -} - -TEST_F(GenerateWebGPUInitializersTest, AmbigiousStructs) { - std::vector<const char*> input_spirv = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", - "%_struct_3 = OpTypeStruct %uint", - "%_struct_4 = OpTypeStruct %uint", -"%_ptr_Private__struct_3 = OpTypePointer Private %_struct_3", -"%_ptr_Private__struct_4 = OpTypePointer Private %_struct_4", - "%7 = OpConstantNull %_struct_3", - "%8 = OpVariable %_ptr_Private__struct_3 Private %7", - "%9 = OpVariable %_ptr_Private__struct_4 Private", - "%void = OpTypeVoid", - "%11 = OpTypeFunction %void", - "%1 = OpFunction %void None %11", - "%12 = OpLabel", - "OpReturn", - "OpFunctionEnd" - // clang-format on - }; - std::string input_str = JoinAllInsts(input_spirv); - - std::vector<const char*> expected_spirv = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", - "%_struct_3 = OpTypeStruct %uint", - "%_struct_4 = OpTypeStruct %uint", -"%_ptr_Private__struct_3 = OpTypePointer Private %_struct_3", -"%_ptr_Private__struct_4 = OpTypePointer Private %_struct_4", - "%7 = OpConstantNull %_struct_3", - "%8 = OpVariable %_ptr_Private__struct_3 Private %7", - "%13 = OpConstantNull %_struct_4", - "%9 = OpVariable %_ptr_Private__struct_4 Private %13", - "%void = OpTypeVoid", - "%11 = OpTypeFunction %void", - "%1 = OpFunction %void None %11", - "%12 = OpLabel", - "OpReturn", - "OpFunctionEnd" - // clang-format on - }; - std::string expected_str = JoinAllInsts(expected_spirv); - - SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(input_str, expected_str, - /* skip_nop = */ false); -} - -} // namespace -} // namespace opt -} // namespace spvtools diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index d3ef09c3..29399013 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp @@ -2581,6 +2581,63 @@ OpFunctionEnd SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true); } +TEST_F(InlineTest, InlineFuncWithOpTerminateRayNotInContinue) { + const std::string text = + R"( + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint AnyHitKHR %MyAHitMain2 "MyAHitMain2" %a + OpSource HLSL 630 + OpName %a "a" + OpName %MyAHitMain2 "MyAHitMain2" + OpName %param_var_a "param.var.a" + OpName %src_MyAHitMain2 "src.MyAHitMain2" + OpName %a_0 "a" + OpName %bb_entry "bb.entry" + %int = OpTypeInt 32 1 +%_ptr_IncomingRayPayloadKHR_int = OpTypePointer IncomingRayPayloadKHR %int + %void = OpTypeVoid + %6 = OpTypeFunction %void +%_ptr_Function_int = OpTypePointer Function %int + %14 = OpTypeFunction %void %_ptr_Function_int + %a = OpVariable %_ptr_IncomingRayPayloadKHR_int IncomingRayPayloadKHR +%MyAHitMain2 = OpFunction %void None %6 + %7 = OpLabel +%param_var_a = OpVariable %_ptr_Function_int Function + %10 = OpLoad %int %a + OpStore %param_var_a %10 + %11 = OpFunctionCall %void %src_MyAHitMain2 %param_var_a + %13 = OpLoad %int %param_var_a + OpStore %a %13 + OpReturn + OpFunctionEnd +%src_MyAHitMain2 = OpFunction %void None %14 + %a_0 = OpFunctionParameter %_ptr_Function_int + %bb_entry = OpLabel + %17 = OpLoad %int %a_0 + OpStore %a %17 + OpTerminateRayKHR + OpFunctionEnd + +; CHECK: %MyAHitMain2 = OpFunction %void None +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %param_var_a = OpVariable %_ptr_Function_int Function +; CHECK-NEXT: OpLoad %int %a +; CHECK-NEXT: OpStore %param_var_a {{%\d+}} +; CHECK-NEXT: OpLoad %int %param_var_a +; CHECK-NEXT: OpStore %a {{%\d+}} +; CHECK-NEXT: OpTerminateRayKHR +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpLoad %int %param_var_a +; CHECK-NEXT: OpStore %a %16 +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +)"; + + SinglePassRunAndMatch<InlineExhaustivePass>(text, false); +} + TEST_F(InlineTest, EarlyReturnFunctionInlined) { // #version 140 // diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp index f1899627..1a42329b 100644 --- a/test/opt/inst_bindless_check_test.cpp +++ b/test/opt/inst_bindless_check_test.cpp @@ -7308,15 +7308,15 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %_ptr_StorageBuffer__struct_128 = OpTypePointer StorageBuffer %_struct_128 ;CHECK: %130 = OpVariable %_ptr_StorageBuffer__struct_128 StorageBuffer ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint - ;CHECK: %uint_3 = OpConstant %uint 3 + ;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %148 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_struct_155 = OpTypeStruct %uint %_runtimearr_uint ;CHECK: %_ptr_StorageBuffer__struct_155 = OpTypePointer StorageBuffer %_struct_155 ;CHECK: %157 = OpVariable %_ptr_StorageBuffer__struct_155 StorageBuffer ;CHECK: %uint_11 = OpConstant %uint 11 - ;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 + ;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -7352,7 +7352,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %146 = OpLoad %v2float %86 ;CHECK: OpBranch %143 ;CHECK: %145 = OpLabel - ;CHECK: %201 = OpFunctionCall %void %147 %uint_71 %uint_3 %uint_0 %119 %140 + ;CHECK: %201 = OpFunctionCall %void %147 %uint_71 %uint_4 %uint_0 %119 %140 ;CHECK: OpBranch %143 ;CHECK: %143 = OpLabel ;CHECK: %203 = OpPhi %v2float %146 %144 %202 %145 @@ -7369,7 +7369,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %209 = OpLoad %v2float %89 ;CHECK: OpBranch %206 ;CHECK: %208 = OpLabel - ;CHECK: %211 = OpFunctionCall %void %147 %uint_75 %uint_3 %uint_0 %204 %140 + ;CHECK: %211 = OpFunctionCall %void %147 %uint_75 %uint_4 %uint_0 %204 %140 ;CHECK: OpBranch %206 ;CHECK: %206 = OpLabel ;CHECK: %212 = OpPhi %v2float %209 %207 %202 %208 @@ -7409,49 +7409,49 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %153 = OpFunctionParameter %uint ;CHECK: %154 = OpLabel ;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_0 - ;CHECK: %161 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11 - ;CHECK: %162 = OpIAdd %uint %161 %uint_11 - ;CHECK: %163 = OpArrayLength %uint %157 1 - ;CHECK: %164 = OpULessThanEqual %bool %162 %163 - ;CHECK: OpSelectionMerge %165 None - ;CHECK: OpBranchConditional %164 %166 %165 - ;CHECK: %166 = OpLabel - ;CHECK: %167 = OpIAdd %uint %161 %uint_0 - ;CHECK: %168 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %167 - ;CHECK: OpStore %168 %uint_11 - ;CHECK: %170 = OpIAdd %uint %161 %uint_1 - ;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %170 - ;CHECK: OpStore %171 %uint_23 - ;CHECK: %173 = OpIAdd %uint %161 %uint_2 - ;CHECK: %174 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %173 - ;CHECK: OpStore %174 %149 - ;CHECK: %175 = OpIAdd %uint %161 %uint_3 + ;CHECK: %160 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11 + ;CHECK: %161 = OpIAdd %uint %160 %uint_11 + ;CHECK: %162 = OpArrayLength %uint %157 1 + ;CHECK: %163 = OpULessThanEqual %bool %161 %162 + ;CHECK: OpSelectionMerge %164 None + ;CHECK: OpBranchConditional %163 %165 %164 + ;CHECK: %165 = OpLabel + ;CHECK: %166 = OpIAdd %uint %160 %uint_0 + ;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %166 + ;CHECK: OpStore %167 %uint_11 + ;CHECK: %169 = OpIAdd %uint %160 %uint_1 + ;CHECK: %170 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %169 + ;CHECK: OpStore %170 %uint_23 + ;CHECK: %172 = OpIAdd %uint %160 %uint_2 + ;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %172 + ;CHECK: OpStore %173 %149 + ;CHECK: %175 = OpIAdd %uint %160 %uint_3 ;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %175 ;CHECK: OpStore %176 %uint_4 ;CHECK: %179 = OpLoad %v4float %gl_FragCoord ;CHECK: %181 = OpBitcast %v4uint %179 ;CHECK: %182 = OpCompositeExtract %uint %181 0 - ;CHECK: %183 = OpIAdd %uint %161 %uint_4 + ;CHECK: %183 = OpIAdd %uint %160 %uint_4 ;CHECK: %184 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %183 ;CHECK: OpStore %184 %182 ;CHECK: %185 = OpCompositeExtract %uint %181 1 - ;CHECK: %187 = OpIAdd %uint %161 %uint_5 + ;CHECK: %187 = OpIAdd %uint %160 %uint_5 ;CHECK: %188 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %187 ;CHECK: OpStore %188 %185 - ;CHECK: %189 = OpIAdd %uint %161 %uint_7 + ;CHECK: %189 = OpIAdd %uint %160 %uint_7 ;CHECK: %190 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %189 ;CHECK: OpStore %190 %150 - ;CHECK: %192 = OpIAdd %uint %161 %uint_8 + ;CHECK: %192 = OpIAdd %uint %160 %uint_8 ;CHECK: %193 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %192 ;CHECK: OpStore %193 %151 - ;CHECK: %195 = OpIAdd %uint %161 %uint_9 + ;CHECK: %195 = OpIAdd %uint %160 %uint_9 ;CHECK: %196 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %195 ;CHECK: OpStore %196 %152 - ;CHECK: %198 = OpIAdd %uint %161 %uint_10 + ;CHECK: %198 = OpIAdd %uint %160 %uint_10 ;CHECK: %199 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %198 ;CHECK: OpStore %199 %153 - ;CHECK: OpBranch %165 - ;CHECK: %165 = OpLabel + ;CHECK: OpBranch %164 + ;CHECK: %164 = OpLabel ;CHECK: OpReturn ;CHECK: OpFunctionEnd )"; @@ -7596,14 +7596,14 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %132 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK:%_struct_139 = OpTypeStruct %uint %_runtimearr_uint ;CHECK:%_ptr_StorageBuffer__struct_139 = OpTypePointer StorageBuffer %_struct_139 ;CHECK: %141 = OpVariable %_ptr_StorageBuffer__struct_139 StorageBuffer ;CHECK: %uint_11 = OpConstant %uint 11 -;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %uint_23 = OpConstant %uint 23 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -7637,7 +7637,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %130 = OpLoad %v2float %81 ;CHECK: OpBranch %127 ;CHECK: %129 = OpLabel -;CHECK: %184 = OpFunctionCall %void %131 %uint_78 %uint_3 %uint_0 %101 %123 +;CHECK: %184 = OpFunctionCall %void %131 %uint_78 %uint_4 %uint_0 %101 %123 ;CHECK: OpBranch %127 ;CHECK: %127 = OpLabel ;CHECK: %186 = OpPhi %v2float %130 %128 %185 %129 @@ -7674,49 +7674,49 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %137 = OpFunctionParameter %uint ;CHECK: %138 = OpLabel ;CHECK: %142 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_0 -;CHECK: %145 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11 -;CHECK: %146 = OpIAdd %uint %145 %uint_11 -;CHECK: %147 = OpArrayLength %uint %141 1 -;CHECK: %148 = OpULessThanEqual %bool %146 %147 -;CHECK: OpSelectionMerge %149 None -;CHECK: OpBranchConditional %148 %150 %149 -;CHECK: %150 = OpLabel -;CHECK: %151 = OpIAdd %uint %145 %uint_0 -;CHECK: %152 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %151 -;CHECK: OpStore %152 %uint_11 -;CHECK: %154 = OpIAdd %uint %145 %uint_1 -;CHECK: %155 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %154 -;CHECK: OpStore %155 %uint_23 -;CHECK: %156 = OpIAdd %uint %145 %uint_2 -;CHECK: %157 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %156 -;CHECK: OpStore %157 %133 -;CHECK: %158 = OpIAdd %uint %145 %uint_3 +;CHECK: %144 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11 +;CHECK: %145 = OpIAdd %uint %144 %uint_11 +;CHECK: %146 = OpArrayLength %uint %141 1 +;CHECK: %147 = OpULessThanEqual %bool %145 %146 +;CHECK: OpSelectionMerge %148 None +;CHECK: OpBranchConditional %147 %149 %148 +;CHECK: %149 = OpLabel +;CHECK: %150 = OpIAdd %uint %144 %uint_0 +;CHECK: %151 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %150 +;CHECK: OpStore %151 %uint_11 +;CHECK: %153 = OpIAdd %uint %144 %uint_1 +;CHECK: %154 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %153 +;CHECK: OpStore %154 %uint_23 +;CHECK: %155 = OpIAdd %uint %144 %uint_2 +;CHECK: %156 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %155 +;CHECK: OpStore %156 %133 +;CHECK: %158 = OpIAdd %uint %144 %uint_3 ;CHECK: %159 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %158 ;CHECK: OpStore %159 %uint_4 ;CHECK: %162 = OpLoad %v4float %gl_FragCoord ;CHECK: %164 = OpBitcast %v4uint %162 ;CHECK: %165 = OpCompositeExtract %uint %164 0 -;CHECK: %166 = OpIAdd %uint %145 %uint_4 +;CHECK: %166 = OpIAdd %uint %144 %uint_4 ;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %166 ;CHECK: OpStore %167 %165 ;CHECK: %168 = OpCompositeExtract %uint %164 1 -;CHECK: %170 = OpIAdd %uint %145 %uint_5 +;CHECK: %170 = OpIAdd %uint %144 %uint_5 ;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %170 ;CHECK: OpStore %171 %168 -;CHECK: %172 = OpIAdd %uint %145 %uint_7 +;CHECK: %172 = OpIAdd %uint %144 %uint_7 ;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %172 ;CHECK: OpStore %173 %134 -;CHECK: %175 = OpIAdd %uint %145 %uint_8 +;CHECK: %175 = OpIAdd %uint %144 %uint_8 ;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %175 ;CHECK: OpStore %176 %135 -;CHECK: %178 = OpIAdd %uint %145 %uint_9 +;CHECK: %178 = OpIAdd %uint %144 %uint_9 ;CHECK: %179 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %178 ;CHECK: OpStore %179 %136 -;CHECK: %181 = OpIAdd %uint %145 %uint_10 +;CHECK: %181 = OpIAdd %uint %144 %uint_10 ;CHECK: %182 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %181 ;CHECK: OpStore %182 %137 -;CHECK: OpBranch %149 -;CHECK: %149 = OpLabel +;CHECK: OpBranch %148 +;CHECK: %148 = OpLabel ;CHECK: OpReturn ;CHECK: OpFunctionEnd )"; @@ -7743,11 +7743,11 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" - OpName %PerBatchEnvMapConstantBuffer_t -"PerBatchEnvMapConstantBuffer_t" OpMemberName %PerBatchEnvMapConstantBuffer_t 0 -"g_matEnvMapWorldToLocal" OpMemberName %PerBatchEnvMapConstantBuffer_t 1 -"g_vEnvironmentMapBoxMins" OpMemberName %PerBatchEnvMapConstantBuffer_t 2 -"g_TexOff" OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" + OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" + OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" + OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" + OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" + OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" OpName %_ "" OpName %PerViewPushConst_t "PerViewPushConst_t" @@ -7831,15 +7831,15 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %135 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK:%_struct_142 = OpTypeStruct %uint %_runtimearr_uint ;CHECK:%_ptr_StorageBuffer__struct_142 = OpTypePointer StorageBuffer %_struct_142 ;CHECK: %144 = OpVariable %_ptr_StorageBuffer__struct_142 StorageBuffer ;CHECK: %uint_11 = OpConstant %uint 11 -;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -7878,13 +7878,15 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %133 = OpLoad %v2float %81 ;CHECK: OpBranch %130 ;CHECK: %132 = OpLabel -;CHECK: %188 = OpFunctionCall %void %134 %uint_78 %uint_3 %uint_0 %101 %126 +;CHECK: %188 = OpFunctionCall %void %134 %uint_78 %uint_4 %uint_0 %101 %126 ;CHECK: OpBranch %130 ;CHECK: %130 = OpLabel ;CHECK: %190 = OpPhi %v2float %133 %131 %189 %132 ;CHECK: %86 = OpFAdd %v2float %66 %190 - %87 = OpLoad %46 %g_tColor %88 = OpLoad %50 %g_sAniso %89 = - OpSampledImage %54 %87 %88 %91 = OpImageSampleImplicitLod %v4float %89 %86 + %87 = OpLoad %46 %g_tColor + %88 = OpLoad %50 %g_sAniso + %89 = OpSampledImage %54 %87 %88 + %91 = OpImageSampleImplicitLod %v4float %89 %86 OpStore %_entryPointOutput_vColor %91 ;CHECK-NOT: %91 = OpImageSampleImplicitLod %v4float %89 %86 ;CHECK-NOT: OpStore %_entryPointOutput_vColor %91 @@ -7931,49 +7933,49 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %140 = OpFunctionParameter %uint ;CHECK: %141 = OpLabel ;CHECK: %145 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_0 -;CHECK: %148 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11 -;CHECK: %149 = OpIAdd %uint %148 %uint_11 -;CHECK: %150 = OpArrayLength %uint %144 1 -;CHECK: %151 = OpULessThanEqual %bool %149 %150 -;CHECK: OpSelectionMerge %152 None -;CHECK: OpBranchConditional %151 %153 %152 -;CHECK: %153 = OpLabel -;CHECK: %154 = OpIAdd %uint %148 %uint_0 -;CHECK: %156 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %154 -;CHECK: OpStore %156 %uint_11 -;CHECK: %158 = OpIAdd %uint %148 %uint_1 -;CHECK: %159 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %158 -;CHECK: OpStore %159 %uint_23 -;CHECK: %160 = OpIAdd %uint %148 %uint_2 -;CHECK: %161 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %160 -;CHECK: OpStore %161 %136 -;CHECK: %162 = OpIAdd %uint %148 %uint_3 +;CHECK: %147 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11 +;CHECK: %148 = OpIAdd %uint %147 %uint_11 +;CHECK: %149 = OpArrayLength %uint %144 1 +;CHECK: %150 = OpULessThanEqual %bool %148 %149 +;CHECK: OpSelectionMerge %151 None +;CHECK: OpBranchConditional %150 %152 %151 +;CHECK: %152 = OpLabel +;CHECK: %153 = OpIAdd %uint %147 %uint_0 +;CHECK: %155 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %153 +;CHECK: OpStore %155 %uint_11 +;CHECK: %157 = OpIAdd %uint %147 %uint_1 +;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %157 +;CHECK: OpStore %158 %uint_23 +;CHECK: %159 = OpIAdd %uint %147 %uint_2 +;CHECK: %160 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %159 +;CHECK: OpStore %160 %136 +;CHECK: %162 = OpIAdd %uint %147 %uint_3 ;CHECK: %163 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %162 ;CHECK: OpStore %163 %uint_4 ;CHECK: %166 = OpLoad %v4float %gl_FragCoord ;CHECK: %168 = OpBitcast %v4uint %166 ;CHECK: %169 = OpCompositeExtract %uint %168 0 -;CHECK: %170 = OpIAdd %uint %148 %uint_4 +;CHECK: %170 = OpIAdd %uint %147 %uint_4 ;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %170 ;CHECK: OpStore %171 %169 ;CHECK: %172 = OpCompositeExtract %uint %168 1 -;CHECK: %174 = OpIAdd %uint %148 %uint_5 +;CHECK: %174 = OpIAdd %uint %147 %uint_5 ;CHECK: %175 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %174 ;CHECK: OpStore %175 %172 -;CHECK: %176 = OpIAdd %uint %148 %uint_7 +;CHECK: %176 = OpIAdd %uint %147 %uint_7 ;CHECK: %177 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %176 ;CHECK: OpStore %177 %137 -;CHECK: %179 = OpIAdd %uint %148 %uint_8 +;CHECK: %179 = OpIAdd %uint %147 %uint_8 ;CHECK: %180 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %179 ;CHECK: OpStore %180 %138 -;CHECK: %182 = OpIAdd %uint %148 %uint_9 +;CHECK: %182 = OpIAdd %uint %147 %uint_9 ;CHECK: %183 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %182 ;CHECK: OpStore %183 %139 -;CHECK: %185 = OpIAdd %uint %148 %uint_10 +;CHECK: %185 = OpIAdd %uint %147 %uint_10 ;CHECK: %186 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %185 ;CHECK: OpStore %186 %140 -;CHECK: OpBranch %152 -;CHECK: %152 = OpLabel +;CHECK: OpBranch %151 +;CHECK: %151 = OpLabel ;CHECK: OpReturn ;CHECK: OpFunctionEnd )"; @@ -8344,15 +8346,15 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %88 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_struct_95 = OpTypeStruct %uint %_runtimearr_uint ;CHECK:%_ptr_StorageBuffer__struct_95 = OpTypePointer StorageBuffer %_struct_95 ;CHECK: %97 = OpVariable %_ptr_StorageBuffer__struct_95 StorageBuffer ;CHECK: %uint_11 = OpConstant %uint 11 -;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -8389,7 +8391,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %86 = OpLoad %v2float %41 ;CHECK: OpBranch %83 ;CHECK: %85 = OpLabel -;CHECK: %141 = OpFunctionCall %void %87 %uint_81 %uint_3 %uint_0 %58 %79 +;CHECK: %141 = OpFunctionCall %void %87 %uint_81 %uint_4 %uint_0 %58 %79 ;CHECK: OpBranch %83 ;CHECK: %83 = OpLabel ;CHECK: %143 = OpPhi %v2float %86 %84 %142 %85 @@ -8424,49 +8426,49 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %93 = OpFunctionParameter %uint ;CHECK: %94 = OpLabel ;CHECK: %98 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_0 -;CHECK: %101 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11 -;CHECK: %102 = OpIAdd %uint %101 %uint_11 -;CHECK: %103 = OpArrayLength %uint %97 1 -;CHECK: %104 = OpULessThanEqual %bool %102 %103 -;CHECK: OpSelectionMerge %105 None -;CHECK: OpBranchConditional %104 %106 %105 -;CHECK: %106 = OpLabel -;CHECK: %107 = OpIAdd %uint %101 %uint_0 -;CHECK: %108 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %107 -;CHECK: OpStore %108 %uint_11 -;CHECK: %110 = OpIAdd %uint %101 %uint_1 -;CHECK: %111 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %110 -;CHECK: OpStore %111 %uint_23 -;CHECK: %113 = OpIAdd %uint %101 %uint_2 -;CHECK: %114 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %113 -;CHECK: OpStore %114 %89 -;CHECK: %115 = OpIAdd %uint %101 %uint_3 +;CHECK: %100 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11 +;CHECK: %101 = OpIAdd %uint %100 %uint_11 +;CHECK: %102 = OpArrayLength %uint %97 1 +;CHECK: %103 = OpULessThanEqual %bool %101 %102 +;CHECK: OpSelectionMerge %104 None +;CHECK: OpBranchConditional %103 %105 %104 +;CHECK: %105 = OpLabel +;CHECK: %106 = OpIAdd %uint %100 %uint_0 +;CHECK: %107 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %106 +;CHECK: OpStore %107 %uint_11 +;CHECK: %109 = OpIAdd %uint %100 %uint_1 +;CHECK: %110 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %109 +;CHECK: OpStore %110 %uint_23 +;CHECK: %112 = OpIAdd %uint %100 %uint_2 +;CHECK: %113 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %112 +;CHECK: OpStore %113 %89 +;CHECK: %115 = OpIAdd %uint %100 %uint_3 ;CHECK: %116 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %115 ;CHECK: OpStore %116 %uint_4 ;CHECK: %119 = OpLoad %v4float %gl_FragCoord ;CHECK: %121 = OpBitcast %v4uint %119 ;CHECK: %122 = OpCompositeExtract %uint %121 0 -;CHECK: %123 = OpIAdd %uint %101 %uint_4 +;CHECK: %123 = OpIAdd %uint %100 %uint_4 ;CHECK: %124 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %123 ;CHECK: OpStore %124 %122 ;CHECK: %125 = OpCompositeExtract %uint %121 1 -;CHECK: %127 = OpIAdd %uint %101 %uint_5 +;CHECK: %127 = OpIAdd %uint %100 %uint_5 ;CHECK: %128 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %127 ;CHECK: OpStore %128 %125 -;CHECK: %129 = OpIAdd %uint %101 %uint_7 +;CHECK: %129 = OpIAdd %uint %100 %uint_7 ;CHECK: %130 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %129 ;CHECK: OpStore %130 %90 -;CHECK: %132 = OpIAdd %uint %101 %uint_8 +;CHECK: %132 = OpIAdd %uint %100 %uint_8 ;CHECK: %133 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %132 ;CHECK: OpStore %133 %91 -;CHECK: %135 = OpIAdd %uint %101 %uint_9 +;CHECK: %135 = OpIAdd %uint %100 %uint_9 ;CHECK: %136 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %135 ;CHECK: OpStore %136 %92 -;CHECK: %138 = OpIAdd %uint %101 %uint_10 +;CHECK: %138 = OpIAdd %uint %100 %uint_10 ;CHECK: %139 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %138 ;CHECK: OpStore %139 %93 -;CHECK: OpBranch %105 -;CHECK: %105 = OpLabel +;CHECK: OpBranch %104 +;CHECK: %104 = OpLabel ;CHECK: OpReturn ;CHECK: OpFunctionEnd )"; @@ -8557,6 +8559,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { %_ptr_Input_v4float = OpTypePointer Input %v4float %a_position = OpVariable %_ptr_Input_v4float Input ;CHECK; %uint_0 = OpConstant %uint 0 +;CHECK; %uint_16 = OpConstant %uint 16 ;CHECK; %uint_4 = OpConstant %uint 4 ;CHECK; %uint_3 = OpConstant %uint 3 ;CHECK; %37 = OpTypeFunction %uint %uint %uint %uint @@ -8583,10 +8586,13 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK; %uint_10 = OpConstant %uint 10 ;CHECK; %uint_45 = OpConstant %uint 45 ;CHECK; %115 = OpConstantNull %float -;CHECK; %uint_27 = OpConstant %uint 27 %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0 +;CHECK: OpBranch %26 +;CHECK: %26 = OpLabel +;CHECK: OpBranch %25 +;CHECK: %25 = OpLabel %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1 %21 = OpLoad %float %20 ;CHECK-NOT: %21 = OpLoad %float %20 @@ -8602,7 +8608,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK: %61 = OpLoad %float %20 ;CHECK: OpBranch %58 ;CHECK: %60 = OpLabel -;CHECK: %114 = OpFunctionCall %void %62 %uint_45 %uint_3 %uint_0 %35 %55 +;CHECK: %114 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55 ;CHECK: OpBranch %58 ;CHECK: %58 = OpLabel ;CHECK: %116 = OpPhi %float %61 %59 %115 %60 @@ -8790,9 +8796,14 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_45 = OpConstant %uint 45 +;CHECK: %114 = OpConstantNull %float %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0 +;CHECK: OpBranch %26 +;CHECK: %26 = OpLabel +;CHECK: OpBranch %25 +;CHECK: %25 = OpLabel %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1 %21 = OpLoad %float %20 ;CHECK-NOT: %21 = OpLoad %float %20 @@ -8808,7 +8819,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: %61 = OpLoad %float %20 ;CHECK: OpBranch %58 ;CHECK: %60 = OpLabel -;CHECK: %113 = OpFunctionCall %void %62 %uint_45 %uint_3 %uint_0 %35 %55 +;CHECK: %113 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55 ;CHECK: OpBranch %58 ;CHECK: %58 = OpLabel ;CHECK: %115 = OpPhi %float %61 %59 %114 %60 @@ -9032,7 +9043,7 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) { ;CHECK: %70 = OpLoad %v2float %25 ;CHECK: OpBranch %67 ;CHECK: %69 = OpLabel -;CHECK: %123 = OpFunctionCall %void %71 %uint_51 %uint_3 %uint_0 %43 %64 +;CHECK: %123 = OpFunctionCall %void %71 %uint_51 %uint_4 %uint_0 %43 %64 ;CHECK: OpBranch %67 ;CHECK: %67 = OpLabel ;CHECK: %125 = OpPhi %v2float %70 %68 %124 %69 @@ -9167,7 +9178,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %35 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_43 = OpTypeStruct %uint %_runtimearr_uint @@ -9179,11 +9190,11 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 ;CHECK: %uint_5 = OpConstant %uint 5 -;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %uint_8 = OpConstant %uint 8 ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 @@ -9213,7 +9224,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %33 = OpImageRead %v4float %32 %17 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %92 = OpFunctionCall %void %34 %uint_33 %uint_3 %uint_0 %23 %25 +;CHECK: %92 = OpFunctionCall %void %34 %uint_33 %uint_7 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel ;CHECK: %94 = OpPhi %v4float %33 %30 %93 %31 @@ -9244,19 +9255,19 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %63 = OpIAdd %uint %50 %uint_2 ;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63 ;CHECK: OpStore %64 %36 -;CHECK: %65 = OpIAdd %uint %50 %uint_3 -;CHECK: %66 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %65 -;CHECK: OpStore %66 %uint_4 -;CHECK: %69 = OpLoad %v4float %gl_FragCoord -;CHECK: %71 = OpBitcast %v4uint %69 -;CHECK: %72 = OpCompositeExtract %uint %71 0 -;CHECK: %73 = OpIAdd %uint %50 %uint_4 -;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %73 -;CHECK: OpStore %74 %72 -;CHECK: %75 = OpCompositeExtract %uint %71 1 -;CHECK: %77 = OpIAdd %uint %50 %uint_5 -;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %77 -;CHECK: OpStore %78 %75 +;CHECK: %66 = OpIAdd %uint %50 %uint_3 +;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66 +;CHECK: OpStore %67 %uint_4 +;CHECK: %70 = OpLoad %v4float %gl_FragCoord +;CHECK: %72 = OpBitcast %v4uint %70 +;CHECK: %73 = OpCompositeExtract %uint %72 0 +;CHECK: %74 = OpIAdd %uint %50 %uint_4 +;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74 +;CHECK: OpStore %75 %73 +;CHECK: %76 = OpCompositeExtract %uint %72 1 +;CHECK: %78 = OpIAdd %uint %50 %uint_5 +;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78 +;CHECK: OpStore %79 %76 ;CHECK: %80 = OpIAdd %uint %50 %uint_7 ;CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %80 ;CHECK: OpStore %81 %37 @@ -9336,7 +9347,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %34 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_42 = OpTypeStruct %uint %_runtimearr_uint @@ -9348,11 +9359,11 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 ;CHECK: %uint_5 = OpConstant %uint 5 -;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %uint_8 = OpConstant %uint 8 ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 @@ -9380,7 +9391,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: OpImageWrite %32 %14 %18 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %91 = OpFunctionCall %void %33 %uint_34 %uint_3 %uint_0 %23 %25 +;CHECK: %91 = OpFunctionCall %void %33 %uint_34 %uint_7 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel OpReturn @@ -9409,19 +9420,19 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: %62 = OpIAdd %uint %49 %uint_2 ;CHECK: %63 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %62 ;CHECK: OpStore %63 %35 -;CHECK: %64 = OpIAdd %uint %49 %uint_3 -;CHECK: %65 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %64 -;CHECK: OpStore %65 %uint_4 -;CHECK: %68 = OpLoad %v4float %gl_FragCoord -;CHECK: %70 = OpBitcast %v4uint %68 -;CHECK: %71 = OpCompositeExtract %uint %70 0 -;CHECK: %72 = OpIAdd %uint %49 %uint_4 -;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %72 -;CHECK: OpStore %73 %71 -;CHECK: %74 = OpCompositeExtract %uint %70 1 -;CHECK: %76 = OpIAdd %uint %49 %uint_5 -;CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %76 -;CHECK: OpStore %77 %74 +;CHECK: %65 = OpIAdd %uint %49 %uint_3 +;CHECK: %66 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %65 +;CHECK: OpStore %66 %uint_4 +;CHECK: %69 = OpLoad %v4float %gl_FragCoord +;CHECK: %71 = OpBitcast %v4uint %69 +;CHECK: %72 = OpCompositeExtract %uint %71 0 +;CHECK: %73 = OpIAdd %uint %49 %uint_4 +;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %73 +;CHECK: OpStore %74 %72 +;CHECK: %75 = OpCompositeExtract %uint %71 1 +;CHECK: %77 = OpIAdd %uint %49 %uint_5 +;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %77 +;CHECK: OpStore %78 %75 ;CHECK: %79 = OpIAdd %uint %49 %uint_7 ;CHECK: %80 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %79 ;CHECK: OpStore %80 %36 @@ -9500,7 +9511,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %35 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_43 = OpTypeStruct %uint %_runtimearr_uint @@ -9512,6 +9523,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -9521,7 +9533,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_32 = OpConstant %uint 32 -;CHECK: %93 = OpConstantNull %v4float +;CHECK: %94 = OpConstantNull %v4float %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: OpBranch %21 @@ -9546,11 +9558,11 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %33 = OpImageFetch %v4float %32 %17 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %92 = OpFunctionCall %void %34 %uint_32 %uint_3 %uint_0 %23 %25 +;CHECK: %93 = OpFunctionCall %void %34 %uint_32 %uint_6 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel -;CHECK: %94 = OpPhi %v4float %33 %30 %93 %31 -;CHECK: OpStore %x %94 +;CHECK: %95 = OpPhi %v4float %33 %30 %94 %31 +;CHECK: OpStore %x %95 OpReturn OpFunctionEnd ;CHECK: %34 = OpFunction %void None %35 @@ -9577,31 +9589,31 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %63 = OpIAdd %uint %50 %uint_2 ;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63 ;CHECK: OpStore %64 %36 -;CHECK: %65 = OpIAdd %uint %50 %uint_3 -;CHECK: %66 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %65 -;CHECK: OpStore %66 %uint_4 -;CHECK: %69 = OpLoad %v4float %gl_FragCoord -;CHECK: %71 = OpBitcast %v4uint %69 -;CHECK: %72 = OpCompositeExtract %uint %71 0 -;CHECK: %73 = OpIAdd %uint %50 %uint_4 -;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %73 -;CHECK: OpStore %74 %72 -;CHECK: %75 = OpCompositeExtract %uint %71 1 -;CHECK: %77 = OpIAdd %uint %50 %uint_5 -;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %77 -;CHECK: OpStore %78 %75 -;CHECK: %80 = OpIAdd %uint %50 %uint_7 -;CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %80 -;CHECK: OpStore %81 %37 -;CHECK: %83 = OpIAdd %uint %50 %uint_8 -;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %83 -;CHECK: OpStore %84 %38 -;CHECK: %86 = OpIAdd %uint %50 %uint_9 -;CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %86 -;CHECK: OpStore %87 %39 -;CHECK: %89 = OpIAdd %uint %50 %uint_10 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %89 -;CHECK: OpStore %90 %40 +;CHECK: %66 = OpIAdd %uint %50 %uint_3 +;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66 +;CHECK: OpStore %67 %uint_4 +;CHECK: %70 = OpLoad %v4float %gl_FragCoord +;CHECK: %72 = OpBitcast %v4uint %70 +;CHECK: %73 = OpCompositeExtract %uint %72 0 +;CHECK: %74 = OpIAdd %uint %50 %uint_4 +;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74 +;CHECK: OpStore %75 %73 +;CHECK: %76 = OpCompositeExtract %uint %72 1 +;CHECK: %78 = OpIAdd %uint %50 %uint_5 +;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78 +;CHECK: OpStore %79 %76 +;CHECK: %81 = OpIAdd %uint %50 %uint_7 +;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %81 +;CHECK: OpStore %82 %37 +;CHECK: %84 = OpIAdd %uint %50 %uint_8 +;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %84 +;CHECK: OpStore %85 %38 +;CHECK: %87 = OpIAdd %uint %50 %uint_9 +;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %87 +;CHECK: OpStore %88 %39 +;CHECK: %90 = OpIAdd %uint %50 %uint_10 +;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %90 +;CHECK: OpStore %91 %40 ;CHECK: OpBranch %54 ;CHECK: %54 = OpLabel ;CHECK: OpReturn @@ -9669,7 +9681,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_46 = OpTypeStruct %uint %_runtimearr_uint @@ -9681,6 +9693,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -9690,7 +9703,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_34 = OpConstant %uint 34 -;CHECK: %96 = OpConstantNull %v4float +;CHECK: %97 = OpConstantNull %v4float %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: OpBranch %23 @@ -9717,11 +9730,11 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %36 = OpImageFetch %v4float %35 %18 ;CHECK: OpBranch %31 ;CHECK: %33 = OpLabel -;CHECK: %95 = OpFunctionCall %void %37 %uint_34 %uint_3 %uint_0 %25 %27 +;CHECK: %96 = OpFunctionCall %void %37 %uint_34 %uint_6 %uint_0 %25 %27 ;CHECK: OpBranch %31 ;CHECK: %31 = OpLabel -;CHECK: %97 = OpPhi %v4float %36 %32 %96 %33 -;CHECK: OpStore %x %97 +;CHECK: %98 = OpPhi %v4float %36 %32 %97 %33 +;CHECK: OpStore %x %98 OpReturn OpFunctionEnd ;CHECK: %37 = OpFunction %void None %38 @@ -9748,31 +9761,31 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %66 = OpIAdd %uint %53 %uint_2 ;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %66 ;CHECK: OpStore %67 %39 -;CHECK: %68 = OpIAdd %uint %53 %uint_3 -;CHECK: %69 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %68 -;CHECK: OpStore %69 %uint_4 -;CHECK: %72 = OpLoad %v4float %gl_FragCoord -;CHECK: %74 = OpBitcast %v4uint %72 -;CHECK: %75 = OpCompositeExtract %uint %74 0 -;CHECK: %76 = OpIAdd %uint %53 %uint_4 -;CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %76 -;CHECK: OpStore %77 %75 -;CHECK: %78 = OpCompositeExtract %uint %74 1 -;CHECK: %80 = OpIAdd %uint %53 %uint_5 -;CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %80 -;CHECK: OpStore %81 %78 -;CHECK: %83 = OpIAdd %uint %53 %uint_7 -;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %83 -;CHECK: OpStore %84 %40 -;CHECK: %86 = OpIAdd %uint %53 %uint_8 -;CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %86 -;CHECK: OpStore %87 %41 -;CHECK: %89 = OpIAdd %uint %53 %uint_9 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %89 -;CHECK: OpStore %90 %42 -;CHECK: %92 = OpIAdd %uint %53 %uint_10 -;CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %92 -;CHECK: OpStore %93 %43 +;CHECK: %69 = OpIAdd %uint %53 %uint_3 +;CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %69 +;CHECK: OpStore %70 %uint_4 +;CHECK: %73 = OpLoad %v4float %gl_FragCoord +;CHECK: %75 = OpBitcast %v4uint %73 +;CHECK: %76 = OpCompositeExtract %uint %75 0 +;CHECK: %77 = OpIAdd %uint %53 %uint_4 +;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %77 +;CHECK: OpStore %78 %76 +;CHECK: %79 = OpCompositeExtract %uint %75 1 +;CHECK: %81 = OpIAdd %uint %53 %uint_5 +;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %81 +;CHECK: OpStore %82 %79 +;CHECK: %84 = OpIAdd %uint %53 %uint_7 +;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %84 +;CHECK: OpStore %85 %40 +;CHECK: %87 = OpIAdd %uint %53 %uint_8 +;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %87 +;CHECK: OpStore %88 %41 +;CHECK: %90 = OpIAdd %uint %53 %uint_9 +;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %90 +;CHECK: OpStore %91 %42 +;CHECK: %93 = OpIAdd %uint %53 %uint_10 +;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %93 +;CHECK: OpStore %94 %43 ;CHECK: OpBranch %57 ;CHECK: %57 = OpLabel ;CHECK: OpReturn @@ -9847,7 +9860,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %44 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_52 = OpTypeStruct %uint %_runtimearr_uint @@ -9859,6 +9872,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -9868,7 +9882,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_42 = OpConstant %uint 42 -;CHECK: %102 = OpConstantNull %v4float +;CHECK: %103 = OpConstantNull %v4float %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: OpBranch %28 @@ -9898,11 +9912,11 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %42 = OpImageFetch %v4float %41 %23 ;CHECK: OpBranch %36 ;CHECK: %38 = OpLabel -;CHECK: %101 = OpFunctionCall %void %43 %uint_42 %uint_3 %uint_0 %30 %32 +;CHECK: %102 = OpFunctionCall %void %43 %uint_42 %uint_6 %uint_0 %30 %32 ;CHECK: OpBranch %36 ;CHECK: %36 = OpLabel -;CHECK: %103 = OpPhi %v4float %42 %37 %102 %38 -;CHECK: OpStore %x %103 +;CHECK: %104 = OpPhi %v4float %42 %37 %103 %38 +;CHECK: OpStore %x %104 OpReturn OpFunctionEnd ;CHECK: %43 = OpFunction %void None %44 @@ -9929,31 +9943,31 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %72 = OpIAdd %uint %59 %uint_2 ;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %72 ;CHECK: OpStore %73 %45 -;CHECK: %74 = OpIAdd %uint %59 %uint_3 -;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %74 -;CHECK: OpStore %75 %uint_4 -;CHECK: %78 = OpLoad %v4float %gl_FragCoord -;CHECK: %80 = OpBitcast %v4uint %78 -;CHECK: %81 = OpCompositeExtract %uint %80 0 -;CHECK: %82 = OpIAdd %uint %59 %uint_4 -;CHECK: %83 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %82 -;CHECK: OpStore %83 %81 -;CHECK: %84 = OpCompositeExtract %uint %80 1 -;CHECK: %86 = OpIAdd %uint %59 %uint_5 -;CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %86 -;CHECK: OpStore %87 %84 -;CHECK: %89 = OpIAdd %uint %59 %uint_7 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %89 -;CHECK: OpStore %90 %46 -;CHECK: %92 = OpIAdd %uint %59 %uint_8 -;CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %92 -;CHECK: OpStore %93 %47 -;CHECK: %95 = OpIAdd %uint %59 %uint_9 -;CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %95 -;CHECK: OpStore %96 %48 -;CHECK: %98 = OpIAdd %uint %59 %uint_10 -;CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %98 -;CHECK: OpStore %99 %49 +;CHECK: %75 = OpIAdd %uint %59 %uint_3 +;CHECK: %76 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %75 +;CHECK: OpStore %76 %uint_4 +;CHECK: %79 = OpLoad %v4float %gl_FragCoord +;CHECK: %81 = OpBitcast %v4uint %79 +;CHECK: %82 = OpCompositeExtract %uint %81 0 +;CHECK: %83 = OpIAdd %uint %59 %uint_4 +;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %83 +;CHECK: OpStore %84 %82 +;CHECK: %85 = OpCompositeExtract %uint %81 1 +;CHECK: %87 = OpIAdd %uint %59 %uint_5 +;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %87 +;CHECK: OpStore %88 %85 +;CHECK: %90 = OpIAdd %uint %59 %uint_7 +;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %90 +;CHECK: OpStore %91 %46 +;CHECK: %93 = OpIAdd %uint %59 %uint_8 +;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %93 +;CHECK: OpStore %94 %47 +;CHECK: %96 = OpIAdd %uint %59 %uint_9 +;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %96 +;CHECK: OpStore %97 %48 +;CHECK: %99 = OpIAdd %uint %59 %uint_10 +;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %99 +;CHECK: OpStore %100 %49 ;CHECK: OpBranch %63 ;CHECK: %63 = OpLabel ;CHECK: OpReturn diff --git a/test/opt/legalize_vector_shuffle_test.cpp b/test/opt/legalize_vector_shuffle_test.cpp deleted file mode 100644 index 07d96eb3..00000000 --- a/test/opt/legalize_vector_shuffle_test.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// 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 <vector> - -#include "test/opt/pass_fixture.h" -#include "test/opt/pass_utils.h" - -namespace spvtools { -namespace opt { -namespace { - -using LegalizeVectorShuffleTest = PassTest<::testing::Test>; - -void operator+=(std::vector<const char*>& lhs, const char* rhs) { - lhs.push_back(rhs); -} - -void operator+=(std::vector<const char*>& lhs, - const std::vector<const char*> rhs) { - for (auto elem : rhs) lhs.push_back(elem); -} - -std::vector<const char*> header = { - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", - "%v3uint = OpTypeVector %uint 3"}; - -std::string GetTestString(const char* shuffle) { - std::vector<const char*> result = header; - result += {"%_ptr_Function_v3uint = OpTypePointer Function %v3uint", - "%void = OpTypeVoid", - "%6 = OpTypeFunction %void", - "%1 = OpFunction %void None %6", - "%7 = OpLabel", - "%8 = OpVariable %_ptr_Function_v3uint Function", - "%9 = OpLoad %v3uint %8", - "%10 = OpLoad %v3uint %8"}; - result += shuffle; - result += {"OpReturn", "OpFunctionEnd"}; - return JoinAllInsts(result); -} - -TEST_F(LegalizeVectorShuffleTest, Changed) { - std::string input = - GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0xFFFFFFFF"); - std::string expected = - GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0"); - - SinglePassRunAndCheck<LegalizeVectorShufflePass>(input, expected, - /* skip_nop = */ false); -} - -TEST_F(LegalizeVectorShuffleTest, FunctionUnchanged) { - std::string input = - GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0"); - std::string expected = - GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0"); - - SinglePassRunAndCheck<LegalizeVectorShufflePass>(input, expected, - /* skip_nop = */ false); -} - -} // namespace -} // namespace opt -} // namespace spvtools diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp index d015dfba..c94ff372 100644 --- a/test/opt/local_single_store_elim_test.cpp +++ b/test/opt/local_single_store_elim_test.cpp @@ -1403,18 +1403,20 @@ TEST_F(LocalSingleStoreElimTest, AddDebugValueforStoreOutOfDebugDeclareScope) { %param_var_pos = OpVariable %_ptr_Function_v4float Function %param_var_color = OpVariable %_ptr_Function_v4float Function %55 = OpLoad %v4float %in_var_POSITION + OpLine %7 6 23 OpStore %param_var_pos %55 + OpNoLine %56 = OpLoad %v4float %in_var_COLOR ;CHECK: DebugNoScope ;CHECK-NOT: OpLine ;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION ;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR + OpLine %7 7 23 OpStore %param_var_color %56 + OpNoLine %93 = OpExtInst %void %1 DebugScope %48 - OpLine %7 6 23 %73 = OpExtInst %void %1 DebugDeclare %53 %param_var_pos %52 - OpLine %7 7 23 %74 = OpExtInst %void %1 DebugDeclare %51 %param_var_color %52 ;CHECK: OpLine [[file:%\w+]] 6 23 ;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_pos]] [[pos]] [[empty_expr:%\w+]] diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp index 9baf8da8..ca9aba33 100644 --- a/test/opt/local_ssa_elim_test.cpp +++ b/test/opt/local_ssa_elim_test.cpp @@ -2261,9 +2261,13 @@ TEST_F(LocalSSAElimTest, AddDebugValueForFunctionParameterWithPhi) { %69 = OpExtInst %void %1 DebugLexicalBlock %60 7 21 %68 %70 = OpExtInst %void %1 DebugLocalVariable %11 %59 %60 6 26 %67 FlagIsLocal 2 +; CHECK: [[color_name:%\w+]] = OpString "color" +; CHECK: [[pos_name:%\w+]] = OpString "pos" ; CHECK: [[i_name:%\w+]] = OpString "i" ; CHECK: [[null_expr:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugExpression ; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] {{%\w+}} {{%\w+}} 6 16 {{%\w+}} FlagIsLocal 1 +; CHECK: [[dbg_color:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[color_name]] {{%\w+}} {{%\w+}} 15 23 +; CHECK: [[dbg_pos:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[pos_name]] {{%\w+}} {{%\w+}} 14 23 %71 = OpExtInst %void %1 DebugLocalVariable %15 %65 %60 6 16 %67 FlagIsLocal 1 %72 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %62 %59 %59 %73 = OpExtInst %void %1 DebugFunction %16 %72 %60 14 1 %61 %14 FlagIsProtected|FlagIsPrivate 15 %156 @@ -2282,13 +2286,23 @@ TEST_F(LocalSSAElimTest, AddDebugValueForFunctionParameterWithPhi) { %169 = OpExtInst %void %1 DebugNoScope %param_var_pos = OpVariable %_ptr_Function_v4float Function %param_var_color = OpVariable %_ptr_Function_v4float Function + OpLine %7 100 105 %80 = OpLoad %v4float %in_var_POSITION OpStore %param_var_pos %80 + OpNoLine + OpLine %7 200 205 %81 = OpLoad %v4float %in_var_COLOR OpStore %param_var_color %81 + OpNoLine %170 = OpExtInst %void %1 DebugScope %73 + +; CHECK: OpLine {{%\w+}} 100 105 +; CHECK: DebugValue [[dbg_pos]] %124 = OpExtInst %void %1 DebugDeclare %78 %param_var_pos %77 +; CHECK: OpLine {{%\w+}} 200 205 +; CHECK: DebugValue [[dbg_color]] %125 = OpExtInst %void %1 DebugDeclare %76 %param_var_color %77 + %171 = OpExtInst %void %1 DebugScope %74 OpLine %7 17 18 diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp index 945aa782..a51638a1 100644 --- a/test/opt/optimizer_test.cpp +++ b/test/opt/optimizer_test.cpp @@ -221,545 +221,171 @@ TEST(Optimizer, CanRegisterPassesFromFlags) { EXPECT_EQ(msg_level, SPV_MSG_ERROR); } -TEST(Optimizer, VulkanToWebGPUSetsCorrectPasses) { - Optimizer opt(SPV_ENV_VULKAN_1_1); - opt.RegisterVulkanToWebGPUPasses(); - std::vector<const char*> pass_names = opt.GetPassNames(); - - std::vector<std::string> registered_passes; - for (auto name = pass_names.begin(); name != pass_names.end(); ++name) - registered_passes.push_back(*name); - - std::vector<std::string> expected_passes = {"eliminate-dead-branches", - "eliminate-dead-code-aggressive", - "eliminate-dead-const", - "flatten-decorations", - "strip-atomic-counter-memory", - "generate-webgpu-initializers", - "legalize-vector-shuffle", - "split-invalid-unreachable", - "compact-ids"}; - std::sort(registered_passes.begin(), registered_passes.end()); - std::sort(expected_passes.begin(), expected_passes.end()); - - ASSERT_EQ(registered_passes.size(), expected_passes.size()); - for (size_t i = 0; i < registered_passes.size(); i++) - EXPECT_EQ(registered_passes[i], expected_passes[i]); -} -struct VulkanToWebGPUPassCase { - // Input SPIR-V - std::string input; - // Expected result SPIR-V - std::string expected; - // Specific pass under test, used for logging messages. - std::string pass; -}; +TEST(Optimizer, RemoveNop) { + // Test that OpNops are removed even if no optimizations are run. + const std::string before = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%3 = OpFunction %void None %2 +%4 = OpLabel +OpNop +OpReturn +OpFunctionEnd +)"; -using VulkanToWebGPUPassTest = - PassTest<::testing::TestWithParam<VulkanToWebGPUPassCase>>; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%3 = OpFunction %void None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; -TEST_P(VulkanToWebGPUPassTest, Ran) { std::vector<uint32_t> binary; { SpirvTools tools(SPV_ENV_VULKAN_1_1); - tools.Assemble(GetParam().input, &binary); + tools.Assemble(before, &binary); } Optimizer opt(SPV_ENV_VULKAN_1_1); - opt.RegisterVulkanToWebGPUPasses(); std::vector<uint32_t> optimized; class ValidatorOptions validator_options; ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, validator_options, true)) - << GetParam().input << "\n"; + << before << "\n"; std::string disassembly; { - SpirvTools tools(SPV_ENV_WEBGPU_0); + SpirvTools tools(SPV_ENV_VULKAN_1_1); tools.Disassemble(optimized.data(), optimized.size(), &disassembly); } - EXPECT_EQ(GetParam().expected, disassembly) - << "Was expecting pass '" << GetParam().pass << "' to have been run.\n"; -} - -INSTANTIATE_TEST_SUITE_P( - Optimizer, VulkanToWebGPUPassTest, - ::testing::ValuesIn(std::vector<VulkanToWebGPUPassCase>{ - // FlattenDecorations - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Fragment %main \"main\" %hue %saturation %value\n" - "OpExecutionMode %main OriginUpperLeft\n" - "OpDecorate %group Flat\n" - "OpDecorate %group NoPerspective\n" - "%group = OpDecorationGroup\n" - "%void = OpTypeVoid\n" - "%void_fn = OpTypeFunction %void\n" - "%float = OpTypeFloat 32\n" - "%_ptr_Input_float = OpTypePointer Input %float\n" - "%hue = OpVariable %_ptr_Input_float Input\n" - "%saturation = OpVariable %_ptr_Input_float Input\n" - "%value = OpVariable %_ptr_Input_float Input\n" - "%main = OpFunction %void None %void_fn\n" - "%entry = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Fragment %1 \"main\" %2 %3 %4\n" - "OpExecutionMode %1 OriginUpperLeft\n" - "%void = OpTypeVoid\n" - "%6 = OpTypeFunction %void\n" - "%float = OpTypeFloat 32\n" - "%_ptr_Input_float = OpTypePointer Input %float\n" - "%2 = OpVariable %_ptr_Input_float Input\n" - "%3 = OpVariable %_ptr_Input_float Input\n" - "%4 = OpVariable %_ptr_Input_float Input\n" - "%1 = OpFunction %void None %6\n" - "%9 = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // pass - "flatten-decorations"}, - // Eliminate Dead Constants - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %func \"shader\"\n" - "%u32 = OpTypeInt 32 0\n" - "%u32_ptr = OpTypePointer Workgroup %u32\n" - "%u32_var = OpVariable %u32_ptr Workgroup\n" - "%u32_1 = OpConstant %u32 1\n" - "%cross_device = OpConstant %u32 0\n" - "%relaxed = OpConstant %u32 0\n" - "%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288\n" - "%void = OpTypeVoid\n" - "%void_f = OpTypeFunction %void\n" - "%func = OpFunction %void None %void_f\n" - "%label = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n" - "%4 = OpVariable %_ptr_Workgroup_uint Workgroup\n" - "%void = OpTypeVoid\n" - "%6 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %6\n" - "%7 = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - "eliminate-dead-const"}, - // Strip Atomic Counter Memory - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %func \"shader\"\n" - "%u32 = OpTypeInt 32 0\n" - "%u32_ptr = OpTypePointer Workgroup %u32\n" - "%u32_var = OpVariable %u32_ptr Workgroup\n" - "%u32_0 = OpConstant %u32 0\n" - "%u32_1 = OpConstant %u32 1\n" - "%cross_device = OpConstant %u32 0\n" - "%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288\n" - "%void = OpTypeVoid\n" - "%void_f = OpTypeFunction %void\n" - "%func = OpFunction %void None %void_f\n" - "%label = OpLabel\n" - " OpAtomicStore %u32_var %cross_device " - "%acquire_release_atomic_counter_workgroup %u32_1\n" - "%val1 = OpAtomicIIncrement %u32 %u32_var %cross_device " - "%acquire_release_atomic_counter_workgroup\n" - "%val2 = OpAtomicCompareExchange %u32 %u32_var %cross_device " - "%acquire_release_atomic_counter_workgroup " - "%acquire_release_atomic_counter_workgroup %u32_0 %u32_0\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n" - "%4 = OpVariable %_ptr_Workgroup_uint Workgroup\n" - "%uint_0 = OpConstant %uint 0\n" - "%uint_1 = OpConstant %uint 1\n" - "%uint_0_0 = OpConstant %uint 0\n" - "%void = OpTypeVoid\n" - "%9 = OpTypeFunction %void\n" - "%uint_264 = OpConstant %uint 264\n" - "%1 = OpFunction %void None %9\n" - "%11 = OpLabel\n" - "OpAtomicStore %4 %uint_0_0 %uint_264 %uint_1\n" - "%12 = OpAtomicIIncrement %uint %4 %uint_0_0 %uint_264\n" - "%13 = OpAtomicCompareExchange %uint %4 %uint_0_0 %uint_264 %uint_264 " - "%uint_0 %uint_0\n" - "OpReturn\n" - "OpFunctionEnd\n", - // pass - "strip-atomic-counter-memory"}, - // Generate WebGPU Initializers - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %func \"shader\"\n" - "%u32 = OpTypeInt 32 0\n" - "%u32_ptr = OpTypePointer Private %u32\n" - "%u32_var = OpVariable %u32_ptr Private\n" - "%u32_0 = OpConstant %u32 0\n" - "%void = OpTypeVoid\n" - "%void_f = OpTypeFunction %void\n" - "%func = OpFunction %void None %void_f\n" - "%label = OpLabel\n" - "OpStore %u32_var %u32_0\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%_ptr_Private_uint = OpTypePointer Private %uint\n" - "%4 = OpConstantNull %uint\n" - "%5 = OpVariable %_ptr_Private_uint Private %4\n" - "%uint_0 = OpConstant %uint 0\n" - "%void = OpTypeVoid\n" - "%8 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %8\n" - "%9 = OpLabel\n" - "OpStore %5 %uint_0\n" - "OpReturn\n" - "OpFunctionEnd\n", - // pass - "generate-webgpu-initializers"}, - // Legalize Vector Shuffle - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%v3uint = OpTypeVector %uint 3\n" - "%_ptr_Function_v3uint = OpTypePointer Function %v3uint\n" - "%void = OpTypeVoid\n" - "%6 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %6\n" - "%7 = OpLabel\n" - "%8 = OpVariable %_ptr_Function_v3uint Function\n" - "%9 = OpLoad %v3uint %8\n" - "%10 = OpLoad %v3uint %8\n" - "%11 = OpVectorShuffle %v3uint %9 %10 2 1 0xFFFFFFFF\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%v3uint = OpTypeVector %uint 3\n" - "%_ptr_Function_v3uint = OpTypePointer Function %v3uint\n" - "%void = OpTypeVoid\n" - "%6 = OpTypeFunction %void\n" - "%7 = OpConstantNull %v3uint\n" - "%1 = OpFunction %void None %6\n" - "%8 = OpLabel\n" - "%9 = OpVariable %_ptr_Function_v3uint Function %7\n" - "%10 = OpLoad %v3uint %9\n" - "%11 = OpLoad %v3uint %9\n" - "%12 = OpVectorShuffle %v3uint %10 %11 2 1 0\n" - "OpReturn\n" - "OpFunctionEnd\n", - // pass - "legalize-vector-shuffle"}, - // Split Invalid Unreachable - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%uint_1 = OpConstant %uint 1\n" - "%uint_2 = OpConstant %uint 2\n" - "%void = OpTypeVoid\n" - "%bool = OpTypeBool\n" - "%7 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %7\n" - "%8 = OpLabel\n" - "OpBranch %9\n" - "%9 = OpLabel\n" - "OpLoopMerge %10 %11 None\n" - "OpBranch %12\n" - "%12 = OpLabel\n" - "%13 = OpSLessThan %bool %uint_1 %uint_2\n" - "OpSelectionMerge %11 None\n" - "OpBranchConditional %13 %14 %15\n" - "%14 = OpLabel\n" - "OpReturn\n" - "%15 = OpLabel\n" - "OpReturn\n" - "%10 = OpLabel\n" - "OpUnreachable\n" - "%11 = OpLabel\n" - "OpBranch %9\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%uint_1 = OpConstant %uint 1\n" - "%uint_2 = OpConstant %uint 2\n" - "%void = OpTypeVoid\n" - "%bool = OpTypeBool\n" - "%7 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %7\n" - "%8 = OpLabel\n" - "OpBranch %9\n" - "%9 = OpLabel\n" - "OpLoopMerge %10 %11 None\n" - "OpBranch %12\n" - "%12 = OpLabel\n" - "%13 = OpSLessThan %bool %uint_1 %uint_2\n" - "OpSelectionMerge %14 None\n" - "OpBranchConditional %13 %15 %16\n" - "%15 = OpLabel\n" - "OpReturn\n" - "%16 = OpLabel\n" - "OpReturn\n" - "%10 = OpLabel\n" - "OpUnreachable\n" - "%14 = OpLabel\n" - "OpUnreachable\n" - "%11 = OpLabel\n" - "OpBranch %9\n" - "OpFunctionEnd\n", - // pass - "split-invalid-unreachable"}, - // Compact IDs - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1000 \"shader\"\n" - "%10 = OpTypeVoid\n" - "%100 = OpTypeFunction %10\n" - "%1000 = OpFunction %10 None %100\n" - "%10000 = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%void = OpTypeVoid\n" - "%3 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %3\n" - "%4 = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // pass - "compact-ids"}})); - -TEST(Optimizer, WebGPUToVulkanSetsCorrectPasses) { - Optimizer opt(SPV_ENV_WEBGPU_0); - opt.RegisterWebGPUToVulkanPasses(); - std::vector<const char*> pass_names = opt.GetPassNames(); - - std::vector<std::string> registered_passes; - for (auto name = pass_names.begin(); name != pass_names.end(); ++name) - registered_passes.push_back(*name); - - std::vector<std::string> expected_passes = {"decompose-initialized-variables", - "compact-ids"}; - std::sort(registered_passes.begin(), registered_passes.end()); - std::sort(expected_passes.begin(), expected_passes.end()); - - ASSERT_EQ(registered_passes.size(), expected_passes.size()); - for (size_t i = 0; i < registered_passes.size(); i++) - EXPECT_EQ(registered_passes[i], expected_passes[i]); + EXPECT_EQ(after, disassembly) + << "Was expecting the OpNop to have been removed."; } -struct WebGPUToVulkanPassCase { - // Input SPIR-V - std::string input; - // Expected result SPIR-V - std::string expected; - // Specific pass under test, used for logging messages. - std::string pass; -}; +TEST(Optimizer, AvoidIntegrityCheckForExtraLineInfo) { + // Test that it avoids the integrity check when no optimizations are run and + // OpLines are propagated. + const std::string before = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpString "Test" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint +%6 = OpFunction %void None %3 +%7 = OpLabel +OpLine %1 10 0 +%8 = OpVariable %_ptr_Function_uint Function +OpLine %1 10 0 +%9 = OpVariable %_ptr_Function_uint Function +OpLine %1 20 0 +OpReturn +OpFunctionEnd +)"; -using WebGPUToVulkanPassTest = - PassTest<::testing::TestWithParam<WebGPUToVulkanPassCase>>; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpString "Test" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint +%6 = OpFunction %void None %3 +%7 = OpLabel +OpLine %1 10 0 +%8 = OpVariable %_ptr_Function_uint Function +%9 = OpVariable %_ptr_Function_uint Function +OpLine %1 20 0 +OpReturn +OpFunctionEnd +)"; -TEST_P(WebGPUToVulkanPassTest, Ran) { std::vector<uint32_t> binary; - { - SpirvTools tools(SPV_ENV_WEBGPU_0); - tools.Assemble(GetParam().input, &binary); - } + SpirvTools tools(SPV_ENV_VULKAN_1_1); + tools.Assemble(before, &binary); - Optimizer opt(SPV_ENV_WEBGPU_0); - opt.RegisterWebGPUToVulkanPasses(); + Optimizer opt(SPV_ENV_VULKAN_1_1); std::vector<uint32_t> optimized; class ValidatorOptions validator_options; ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, - validator_options, true)); + validator_options, true)) + << before << "\n"; + std::string disassembly; - { - SpirvTools tools(SPV_ENV_VULKAN_1_1); - tools.Disassemble(optimized.data(), optimized.size(), &disassembly); - } + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); - EXPECT_EQ(GetParam().expected, disassembly) - << "Was expecting pass '" << GetParam().pass << "' to have been run.\n"; + EXPECT_EQ(after, disassembly) + << "Was expecting the OpLine to have been propagated."; } -INSTANTIATE_TEST_SUITE_P( - Optimizer, WebGPUToVulkanPassTest, - ::testing::ValuesIn(std::vector<WebGPUToVulkanPassCase>{ - // Decompose Initialized Variables - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%_ptr_Function_uint = OpTypePointer Function %uint\n" - "%4 = OpConstantNull %uint\n" - "%void = OpTypeVoid\n" - "%6 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %6\n" - "%7 = OpLabel\n" - "%8 = OpVariable %_ptr_Function_uint Function %4\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%uint = OpTypeInt 32 0\n" - "%_ptr_Function_uint = OpTypePointer Function %uint\n" - "%4 = OpConstantNull %uint\n" - "%void = OpTypeVoid\n" - "%6 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %6\n" - "%7 = OpLabel\n" - "%8 = OpVariable %_ptr_Function_uint Function\n" - "OpStore %8 %4\n" - "OpReturn\n" - "OpFunctionEnd\n", - // pass - "decompose-initialized-variables"}, - // Compact IDs - {// input - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1000 \"shader\"\n" - "%10 = OpTypeVoid\n" - "%100 = OpTypeFunction %10\n" - "%1000 = OpFunction %10 None %100\n" - "%10000 = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // expected - "OpCapability Shader\n" - "OpCapability VulkanMemoryModel\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical Vulkan\n" - "OpEntryPoint Vertex %1 \"shader\"\n" - "%void = OpTypeVoid\n" - "%3 = OpTypeFunction %void\n" - "%1 = OpFunction %void None %3\n" - "%4 = OpLabel\n" - "OpReturn\n" - "OpFunctionEnd\n", - // pass - "compact-ids"}})); - -TEST(Optimizer, RemoveNop) { - // Test that OpNops are removed even if no optimizations are run. +TEST(Optimizer, AvoidIntegrityCheckForDebugScope) { + // Test that it avoids the integrity check when the code contains DebugScope. const std::string before = R"(OpCapability Shader -OpCapability Linkage +%1 = OpExtInstImport "OpenCL.DebugInfo.100" OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "simple_vs.hlsl" +OpSource HLSL 600 %3 +OpName %main "main" %void = OpTypeVoid -%2 = OpTypeFunction %void -%3 = OpFunction %void None %2 -%4 = OpLabel -OpNop +%5 = OpTypeFunction %void +%6 = OpExtInst %void %1 DebugSource %3 +%7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL +%main = OpFunction %void None %5 +%14 = OpLabel +%26 = OpExtInst %void %1 DebugScope %7 OpReturn +%27 = OpExtInst %void %1 DebugNoScope OpFunctionEnd )"; const std::string after = R"(OpCapability Shader -OpCapability Linkage +%1 = OpExtInstImport "OpenCL.DebugInfo.100" OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "simple_vs.hlsl" +OpSource HLSL 600 %3 +OpName %main "main" %void = OpTypeVoid -%2 = OpTypeFunction %void -%3 = OpFunction %void None %2 -%4 = OpLabel +%5 = OpTypeFunction %void +%6 = OpExtInst %void %1 DebugSource %3 +%7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL +%main = OpFunction %void None %5 +%8 = OpLabel +%11 = OpExtInst %void %1 DebugScope %7 OpReturn +%12 = OpExtInst %void %1 DebugNoScope OpFunctionEnd )"; std::vector<uint32_t> binary; - { - SpirvTools tools(SPV_ENV_VULKAN_1_1); - tools.Assemble(before, &binary); - } + SpirvTools tools(SPV_ENV_VULKAN_1_1); + tools.Assemble(before, &binary); Optimizer opt(SPV_ENV_VULKAN_1_1); std::vector<uint32_t> optimized; - class ValidatorOptions validator_options; - ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, - validator_options, true)) + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized)) << before << "\n"; + std::string disassembly; - { - SpirvTools tools(SPV_ENV_WEBGPU_0); - tools.Disassemble(optimized.data(), optimized.size(), &disassembly); - } + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); EXPECT_EQ(after, disassembly) - << "Was expecting the OpNop to have been removed."; + << "Was expecting the result id of DebugScope to have been changed."; } } // namespace diff --git a/test/opt/split_invalid_unreachable_test.cpp b/test/opt/split_invalid_unreachable_test.cpp deleted file mode 100644 index 520af015..00000000 --- a/test/opt/split_invalid_unreachable_test.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// 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 <vector> - -#include "test/opt/pass_fixture.h" -#include "test/opt/pass_utils.h" - -namespace spvtools { -namespace opt { -namespace { - -using SplitInvalidUnreachableTest = PassTest<::testing::Test>; - -std::string spirv_header = R"(OpCapability Shader -OpCapability VulkanMemoryModel -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical Vulkan -OpEntryPoint Vertex %1 "shader" -%uint = OpTypeInt 32 0 -%uint_1 = OpConstant %uint 1 -%uint_2 = OpConstant %uint 2 -%void = OpTypeVoid -%bool = OpTypeBool -%7 = OpTypeFunction %void -)"; - -std::string function_head = R"(%1 = OpFunction %void None %7 -%8 = OpLabel -OpBranch %9 -)"; - -std::string function_tail = "OpFunctionEnd\n"; - -std::string GetLoopMergeBlock(std::string block_id, std::string merge_id, - std::string continue_id, std::string body_id) { - std::string result; - result += block_id + " = OpLabel\n"; - result += "OpLoopMerge " + merge_id + " " + continue_id + " None\n"; - result += "OpBranch " + body_id + "\n"; - return result; -} - -std::string GetSelectionMergeBlock(std::string block_id, - std::string condition_id, - std::string merge_id, std::string true_id, - std::string false_id) { - std::string result; - result += block_id + " = OpLabel\n"; - result += condition_id + " = OpSLessThan %bool %uint_1 %uint_2\n"; - result += "OpSelectionMerge " + merge_id + " None\n"; - result += "OpBranchConditional " + condition_id + " " + true_id + " " + - false_id + "\n"; - - return result; -} - -std::string GetReturnBlock(std::string block_id) { - std::string result; - result += block_id + " = OpLabel\n"; - result += "OpReturn\n"; - return result; -} - -std::string GetUnreachableBlock(std::string block_id) { - std::string result; - result += block_id + " = OpLabel\n"; - result += "OpUnreachable\n"; - return result; -} - -std::string GetBranchBlock(std::string block_id, std::string target_id) { - std::string result; - result += block_id + " = OpLabel\n"; - result += "OpBranch " + target_id + "\n"; - return result; -} - -TEST_F(SplitInvalidUnreachableTest, NoInvalidBlocks) { - std::string input = spirv_header + function_head; - input += GetLoopMergeBlock("%9", "%10", "%11", "%12"); - input += GetSelectionMergeBlock("%12", "%13", "%14", "%15", "%16"); - input += GetReturnBlock("%15"); - input += GetReturnBlock("%16"); - input += GetUnreachableBlock("%10"); - input += GetBranchBlock("%11", "%9"); - input += GetUnreachableBlock("%14"); - input += function_tail; - - SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, input, - /* skip_nop = */ false); -} - -TEST_F(SplitInvalidUnreachableTest, SelectionInLoop) { - std::string input = spirv_header + function_head; - input += GetLoopMergeBlock("%9", "%10", "%11", "%12"); - input += GetSelectionMergeBlock("%12", "%13", "%11", "%15", "%16"); - input += GetReturnBlock("%15"); - input += GetReturnBlock("%16"); - input += GetUnreachableBlock("%10"); - input += GetBranchBlock("%11", "%9"); - input += function_tail; - - std::string expected = spirv_header + function_head; - expected += GetLoopMergeBlock("%9", "%10", "%11", "%12"); - expected += GetSelectionMergeBlock("%12", "%13", "%16", "%14", "%15"); - expected += GetReturnBlock("%14"); - expected += GetReturnBlock("%15"); - expected += GetUnreachableBlock("%10"); - expected += GetUnreachableBlock("%16"); - expected += GetBranchBlock("%11", "%9"); - expected += function_tail; - - SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, expected, - /* skip_nop = */ false); -} - -TEST_F(SplitInvalidUnreachableTest, LoopInSelection) { - std::string input = spirv_header + function_head; - input += GetSelectionMergeBlock("%9", "%10", "%11", "%12", "%13"); - input += GetLoopMergeBlock("%12", "%14", "%11", "%15"); - input += GetReturnBlock("%13"); - input += GetUnreachableBlock("%14"); - input += GetBranchBlock("%11", "%12"); - input += GetReturnBlock("%15"); - input += function_tail; - - std::string expected = spirv_header + function_head; - expected += GetSelectionMergeBlock("%9", "%10", "%16", "%12", "%13"); - expected += GetLoopMergeBlock("%12", "%14", "%11", "%15"); - expected += GetReturnBlock("%13"); - expected += GetUnreachableBlock("%14"); - expected += GetUnreachableBlock("%16"); - expected += GetBranchBlock("%11", "%12"); - expected += GetReturnBlock("%15"); - expected += function_tail; - - SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, expected, - /* skip_nop = */ false); -} - -} // namespace -} // namespace opt -} // namespace spvtools diff --git a/test/opt/strip_atomic_counter_memory_test.cpp b/test/opt/strip_atomic_counter_memory_test.cpp deleted file mode 100644 index 90daa59c..00000000 --- a/test/opt/strip_atomic_counter_memory_test.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// 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 <vector> - -#include "test/opt/pass_fixture.h" -#include "test/opt/pass_utils.h" - -namespace spvtools { -namespace opt { -namespace { - -typedef std::tuple<std::string, std::string> StripAtomicCounterMemoryParam; - -using MemorySemanticsModified = - PassTest<::testing::TestWithParam<StripAtomicCounterMemoryParam>>; -using NonMemorySemanticsUnmodifiedTest = PassTest<::testing::Test>; - -void operator+=(std::vector<const char*>& lhs, const char* rhs) { - lhs.push_back(rhs); -} - -std::string GetConstDecl(std::string val) { - std::string decl; - decl += "%uint_" + val + " = OpConstant %uint " + val; - return decl; -} - -std::string GetUnchangedString(std::string(generate_inst)(std::string), - std::string val) { - std::string decl = GetConstDecl(val); - std::string inst = generate_inst(val); - - std::vector<const char*> result = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", -"%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint", - "%4 = OpVariable %_ptr_Workgroup_uint Workgroup", - "%uint_0 = OpConstant %uint 0", - "%uint_1 = OpConstant %uint 1", - "%void = OpTypeVoid", - "%8 = OpTypeFunction %void", - decl.c_str(), - "%1 = OpFunction %void None %8", - "%10 = OpLabel", - inst.c_str(), - "OpReturn", - "OpFunctionEnd" - // clang-format on - }; - return JoinAllInsts(result); -} - -std::string GetChangedString(std::string(generate_inst)(std::string), - std::string orig, std::string changed) { - std::string orig_decl = GetConstDecl(orig); - std::string changed_decl = GetConstDecl(changed); - std::string inst = generate_inst(changed); - - std::vector<const char*> result = { - // clang-format off - "OpCapability Shader", - "OpCapability VulkanMemoryModel", - "OpExtension \"SPV_KHR_vulkan_memory_model\"", - "OpMemoryModel Logical Vulkan", - "OpEntryPoint Vertex %1 \"shader\"", - "%uint = OpTypeInt 32 0", -"%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint", - "%4 = OpVariable %_ptr_Workgroup_uint Workgroup", - "%uint_0 = OpConstant %uint 0", - "%uint_1 = OpConstant %uint 1", - "%void = OpTypeVoid", - "%8 = OpTypeFunction %void", - orig_decl.c_str() }; - // clang-format on - if (changed != "0") result += changed_decl.c_str(); - result += "%1 = OpFunction %void None %8"; - result += "%10 = OpLabel"; - result += inst.c_str(); - result += "OpReturn"; - result += "OpFunctionEnd"; - return JoinAllInsts(result); -} - -std::tuple<std::string, std::string> GetInputAndExpected( - std::string(generate_inst)(std::string), - StripAtomicCounterMemoryParam param) { - std::string orig = std::get<0>(param); - std::string changed = std::get<1>(param); - std::string input = GetUnchangedString(generate_inst, orig); - std::string expected = orig == changed - ? GetUnchangedString(generate_inst, changed) - : GetChangedString(generate_inst, orig, changed); - return std::make_tuple(input, expected); -} - -std::string GetOpControlBarrierInst(std::string val) { - return "OpControlBarrier %uint_1 %uint_1 %uint_" + val; -} - -TEST_P(MemorySemanticsModified, OpControlBarrier) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpControlBarrierInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpMemoryBarrierInst(std::string val) { - return "OpMemoryBarrier %uint_1 %uint_" + val; -} - -TEST_P(MemorySemanticsModified, OpMemoryBarrier) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpMemoryBarrierInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicLoadInst(std::string val) { - return "%11 = OpAtomicLoad %uint %4 %uint_1 %uint_" + val; -} - -TEST_P(MemorySemanticsModified, OpAtomicLoad) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicLoadInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicStoreInst(std::string val) { - return "OpAtomicStore %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicStore) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicStoreInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicExchangeInst(std::string val) { - return "%11 = OpAtomicExchange %uint %4 %uint_1 %uint_" + val + " %uint_0"; -} - -TEST_P(MemorySemanticsModified, OpAtomicExchange) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicExchangeInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicCompareExchangeInst(std::string val) { - return "%11 = OpAtomicCompareExchange %uint %4 %uint_1 %uint_" + val + - " %uint_" + val + " %uint_0 %uint_0"; -} - -TEST_P(MemorySemanticsModified, OpAtomicCompareExchange) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicCompareExchangeInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicCompareExchangeWeakInst(std::string val) { - return "%11 = OpAtomicCompareExchangeWeak %uint %4 %uint_1 %uint_" + val + - " %uint_" + val + " %uint_0 %uint_0"; -} - -TEST_P(MemorySemanticsModified, OpAtomicCompareExchangeWeak) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicCompareExchangeWeakInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicIIncrementInst(std::string val) { - return "%11 = OpAtomicIIncrement %uint %4 %uint_1 %uint_" + val; -} - -TEST_P(MemorySemanticsModified, OpAtomicIIncrement) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicIIncrementInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicIDecrementInst(std::string val) { - return "%11 = OpAtomicIDecrement %uint %4 %uint_1 %uint_" + val; -} - -TEST_P(MemorySemanticsModified, OpAtomicIDecrement) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicIDecrementInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicIAddInst(std::string val) { - return "%11 = OpAtomicIAdd %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicIAdd) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicIAddInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicISubInst(std::string val) { - return "%11 = OpAtomicISub %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicISub) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicISubInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicSMinInst(std::string val) { - return "%11 = OpAtomicSMin %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicSMin) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicSMinInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicUMinInst(std::string val) { - return "%11 = OpAtomicUMin %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicUMin) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicUMinInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicSMaxInst(std::string val) { - return "%11 = OpAtomicSMax %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicSMax) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicSMaxInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicUMaxInst(std::string val) { - return "%11 = OpAtomicUMax %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicUMax) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicUMaxInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicAndInst(std::string val) { - return "%11 = OpAtomicAnd %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicAnd) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicAndInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicOrInst(std::string val) { - return "%11 = OpAtomicOr %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicOr) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicOrInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicXorInst(std::string val) { - return "%11 = OpAtomicXor %uint %4 %uint_1 %uint_" + val + " %uint_1"; -} - -TEST_P(MemorySemanticsModified, OpAtomicXor) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicXorInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicFlagTestAndSetInst(std::string val) { - return "%11 = OpAtomicFlagTestAndSet %uint %4 %uint_1 %uint_" + val; -} - -TEST_P(MemorySemanticsModified, OpAtomicFlagTestAndSet) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicFlagTestAndSetInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpAtomicFlagClearInst(std::string val) { - return "OpAtomicFlagClear %4 %uint_1 %uint_" + val; -} - -TEST_P(MemorySemanticsModified, OpAtomicFlagClear) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpAtomicFlagClearInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetOpMemoryNamedBarrierInst(std::string val) { - return "OpMemoryNamedBarrier %4 %uint_1 %uint_" + val; -} - -TEST_P(MemorySemanticsModified, OpMemoryNamedBarrier) { - std::string input, expected; - std::tie(input, expected) = - GetInputAndExpected(GetOpMemoryNamedBarrierInst, GetParam()); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -// clang-format off -INSTANTIATE_TEST_SUITE_P( - StripAtomicCounterMemoryTest, MemorySemanticsModified, - ::testing::ValuesIn(std::vector<StripAtomicCounterMemoryParam>({ - std::make_tuple("1024", "0"), - std::make_tuple("5", "5"), - std::make_tuple("1288", "264"), - std::make_tuple("264", "264") - }))); -// clang-format on - -std::string GetNoMemorySemanticsPresentInst(std::string val) { - return "%11 = OpVariable %_ptr_Workgroup_uint Workgroup %uint_" + val; -} - -TEST_F(NonMemorySemanticsUnmodifiedTest, NoMemorySemanticsPresent) { - std::string input, expected; - StripAtomicCounterMemoryParam param = std::make_tuple("1288", "1288"); - std::tie(input, expected) = - GetInputAndExpected(GetNoMemorySemanticsPresentInst, param); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -std::string GetMemorySemanticsPresentInst(std::string val) { - return "%11 = OpAtomicIAdd %uint %4 %uint_1 %uint_" + val + " %uint_1288"; -} - -TEST_F(NonMemorySemanticsUnmodifiedTest, MemorySemanticsPresent) { - std::string input, expected; - StripAtomicCounterMemoryParam param = std::make_tuple("1288", "264"); - std::tie(input, expected) = - GetInputAndExpected(GetMemorySemanticsPresentInst, param); - SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected, - /* skip_nop = */ false); -} - -} // namespace -} // namespace opt -} // namespace spvtools diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp index 9c86e2da..4acb8ff2 100644 --- a/test/target_env_test.cpp +++ b/test/target_env_test.cpp @@ -95,7 +95,6 @@ INSTANTIATE_TEST_SUITE_P( {"opencl2.0embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_0}, {"opencl2.1embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_1}, {"opencl2.2embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_2}, - {"webgpu0", true, SPV_ENV_WEBGPU_0}, {"opencl2.3", false, SPV_ENV_UNIVERSAL_1_0}, {"opencl3.0", false, SPV_ENV_UNIVERSAL_1_0}, {"vulkan1.9", false, SPV_ENV_UNIVERSAL_1_0}, diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py index f8117d9d..c79f6807 100644 --- a/test/tools/opt/flags.py +++ b/test/tools/opt/flags.py @@ -73,10 +73,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, '--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite', '--scalar-replacement', '--scalar-replacement=42', '--strength-reduction', '--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209', - '--unify-const', '--legalize-vector-shuffle', - '--split-invalid-unreachable', '--generate-webgpu-initializers', - '--decompose-initialized-variables', '--graphics-robust-access', - '--wrap-opkill', '--amd-ext-to-khr' + '--unify-const', '--graphics-robust-access', '--wrap-opkill', '--amd-ext-to-khr' ] expected_passes = [ 'wrap-opkill', @@ -124,10 +121,6 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, 'vector-dce', 'workaround-1209', 'unify-const', - 'legalize-vector-shuffle', - 'split-invalid-unreachable', - 'generate-webgpu-initializers', - 'decompose-initialized-variables', 'graphics-robust-access', 'wrap-opkill', 'amd-ext-to-khr' @@ -362,45 +355,3 @@ class TestLoopPeelingThresholdArgsInvalidNumber(expect.ErrorMessageSubstr): spirv_args = ['--loop-peeling-threshold=a10f'] expected_error_substr = 'must have a positive integer argument' - -@inside_spirv_testsuite('SpirvOptFlags') -class TestWebGPUToVulkanThenVulkanToWebGPUIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): - """Tests Vulkan->WebGPU flag cannot be used after WebGPU->Vulkan flag.""" - - spirv_args = ['--webgpu-to-vulkan', '--vulkan-to-webgpu'] - expected_error_substr = 'Cannot use both' - -@inside_spirv_testsuite('SpirvOptFlags') -class TestVulkanToWebGPUThenWebGPUToVulkanIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): - """Tests WebGPU->Vulkan flag cannot be used after Vulkan->WebGPU flag.""" - - spirv_args = ['--vulkan-to-webgpu', '--webgpu-to-vulkan'] - expected_error_substr = 'Cannot use both' - -@inside_spirv_testsuite('SpirvOptFlags') -class TestTargetEnvThenVulkanToWebGPUIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): - """Tests Vulkan->WebGPU flag cannot be used after target env flag.""" - - spirv_args = ['--target-env=opengl4.0', '--vulkan-to-webgpu'] - expected_error_substr = 'defines the target environment' - -@inside_spirv_testsuite('SpirvOptFlags') -class TestVulkanToWebGPUThenTargetEnvIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): - """Tests target env flag cannot be used after Vulkan->WebGPU flag.""" - - spirv_args = ['--vulkan-to-webgpu', '--target-env=opengl4.0'] - expected_error_substr = 'defines the target environment' - -@inside_spirv_testsuite('SpirvOptFlags') -class TestTargetEnvThenWebGPUToVulkanIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): - """Tests WebGPU->Vulkan flag cannot be used after target env flag.""" - - spirv_args = ['--target-env=opengl4.0', '--webgpu-to-vulkan'] - expected_error_substr = 'defines the target environment' - -@inside_spirv_testsuite('SpirvOptFlags') -class TestWebGPUToVulkanThenTargetEnvIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): - """Tests target env flag cannot be used after WebGPU->Vulkan flag.""" - - spirv_args = ['--webgpu-to-vulkan', '--target-env=opengl4.0'] - expected_error_substr = 'defines the target environment' diff --git a/test/unit_spirv.h b/test/unit_spirv.h index 32646620..f0a2958d 100644 --- a/test/unit_spirv.h +++ b/test/unit_spirv.h @@ -195,7 +195,7 @@ inline std::vector<spv_target_env> AllTargetEnvironments() { SPV_ENV_OPENGL_4_1, SPV_ENV_OPENGL_4_2, SPV_ENV_OPENGL_4_3, SPV_ENV_OPENGL_4_5, SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, - SPV_ENV_VULKAN_1_1, SPV_ENV_WEBGPU_0, + SPV_ENV_VULKAN_1_1, }; } diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index 153a9167..b17d1cb1 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt @@ -89,7 +89,6 @@ add_spvtools_unittest(TARGET val_stuvw val_type_unique_test.cpp val_validation_state_test.cpp val_version_test.cpp - val_webgpu_test.cpp ${VAL_TEST_COMMON_SRCS} LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} PCH_FILE pch_test_val diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index aca0f3c9..fccfabc3 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp @@ -30,16 +30,16 @@ using ValidateAtomics = spvtest::ValidateBase<bool>; std::string GenerateShaderCodeImpl( const std::string& body, const std::string& capabilities_and_extensions, - const std::string& definitions, const std::string& memory_model) { + const std::string& definitions, const std::string& memory_model, + const std::string& execution) { std::ostringstream ss; ss << R"( OpCapability Shader )"; ss << capabilities_and_extensions; ss << "OpMemoryModel Logical " << memory_model << "\n"; + ss << execution; ss << R"( -OpEntryPoint Fragment %main "main" -OpExecutionMode %main OriginUpperLeft %void = OpTypeVoid %func = OpTypeFunction %void %bool = OpTypeBool @@ -96,6 +96,10 @@ std::string GenerateShaderCode( const std::string& body, const std::string& capabilities_and_extensions = "", const std::string& memory_model = "GLSL450") { + const std::string execution = R"( +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +)"; const std::string defintions = R"( %u64 = OpTypeInt 64 0 %s64 = OpTypeInt 64 1 @@ -110,24 +114,32 @@ std::string GenerateShaderCode( )"; return GenerateShaderCodeImpl( body, "OpCapability Int64\n" + capabilities_and_extensions, defintions, - memory_model); + memory_model, execution); } -std::string GenerateWebGPUShaderCode( +std::string GenerateShaderComputeCode( const std::string& body, - const std::string& capabilities_and_extensions = "") { - const std::string vulkan_memory_capability = R"( -OpCapability VulkanMemoryModelDeviceScopeKHR -OpCapability VulkanMemoryModelKHR + const std::string& capabilities_and_extensions = "", + const std::string& memory_model = "GLSL450") { + const std::string execution = R"( +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 32 1 1 )"; - const std::string vulkan_memory_extension = R"( -OpExtension "SPV_KHR_vulkan_memory_model" + const std::string defintions = R"( +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 + +%u64_1 = OpConstant %u64 1 +%s64_1 = OpConstant %s64 1 + +%u64_ptr = OpTypePointer Workgroup %u64 +%s64_ptr = OpTypePointer Workgroup %s64 +%u64_var = OpVariable %u64_ptr Workgroup +%s64_var = OpVariable %s64_ptr Workgroup )"; - return GenerateShaderCodeImpl(body, - vulkan_memory_capability + - capabilities_and_extensions + - vulkan_memory_extension, - "", "VulkanKHR"); + return GenerateShaderCodeImpl( + body, "OpCapability Int64\n" + capabilities_and_extensions, defintions, + memory_model, execution); } std::string GenerateKernelCode( @@ -234,7 +246,7 @@ TEST_F(ValidateAtomics, AtomicLoadInt32VulkanSuccess) { %val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire )"; - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -358,7 +370,7 @@ TEST_F(ValidateAtomics, AtomicLoadFloatVulkan) { %val2 = OpAtomicLoad %f32 %f32_var %workgroup %acquire )"; - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -386,8 +398,9 @@ TEST_F(ValidateAtomics, AtomicLoadInt64WithCapabilityVulkanSuccess) { %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire )"; - CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n"), - SPV_ENV_VULKAN_1_0); + CompileSuccessfully( + GenerateShaderComputeCode(body, "OpCapability Int64Atomics\n"), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -397,7 +410,7 @@ TEST_F(ValidateAtomics, AtomicLoadInt64WithoutCapabilityVulkan) { %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire )"; - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("64-bit atomics require the Int64Atomics capability")); @@ -422,6 +435,23 @@ OpAtomicStore %f32_var_function %device %relaxed %f32_1 CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04686")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicStore: Vulkan spec only allows storage classes for " + "atomic to be: Uniform, Workgroup, Image, StorageBuffer, or " + "PhysicalStorageBuffer.")); +} + +TEST_F(ValidateAtomics, AtomicStoreFunctionPointerStorageType) { + const std::string body = R"( +%f32_var_function = OpVariable %f32_ptr_function Function +OpAtomicStore %f32_var_function %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("AtomicStore: Function storage class forbidden when " "the Shader capability is declared.")); } @@ -447,6 +477,8 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanRelease) { CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " @@ -460,6 +492,8 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanAcquireRelease) { CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " @@ -473,6 +507,8 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanSequentiallyConsistent) { CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " @@ -501,39 +537,6 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanInt64) { "AtomicLoad: 64-bit atomics require the Int64Atomics capability")); } -TEST_F(ValidateAtomics, AtomicLoadWebGPUSuccess) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %relaxed -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateAtomics, AtomicLoadWebGPUNonQueueFamilyFailure) { - const std::string body = R"( -%val3 = OpAtomicLoad %u32 %u32_var %invocation %relaxed -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Memory Scope is limited to QueueFamilyKHR for " - "OpAtomic* operations")); -} - -TEST_F(ValidateAtomics, AtomicLoadWebGPUNonRelaxedFailure) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %acquire -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("no bits may be set for Memory Semantics of OpAtomic* " - "instructions")); -} - TEST_F(ValidateAtomics, VK_KHR_shader_atomic_int64Success) { const std::string body = R"( %val1 = OpAtomicUMin %u64 %u64_var %device %relaxed %u64_1 @@ -678,6 +681,8 @@ OpAtomicStore %u32_var %device %acquire %u32_1 CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " @@ -691,6 +696,8 @@ OpAtomicStore %u32_var %device %acquire_release %u32_1 CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " @@ -704,44 +711,14 @@ OpAtomicStore %u32_var %device %sequentially_consistent %u32_1 CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " "Acquire, AcquireRelease and SequentiallyConsistent")); } -TEST_F(ValidateAtomics, AtomicStoreWebGPUSuccess) { - const std::string body = R"( -OpAtomicStore %u32_var %queuefamily %relaxed %u32_1 -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} -TEST_F(ValidateAtomics, AtomicStoreWebGPUNonQueueFamilyFailure) { - const std::string body = R"( -OpAtomicStore %u32_var %workgroup %relaxed %u32_1 -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Memory Scope is limited to QueueFamilyKHR for " - "OpAtomic* operations")); -} - -TEST_F(ValidateAtomics, AtomicStoreWebGPUNonRelaxedFailure) { - const std::string body = R"( -OpAtomicStore %u32_var %queuefamily %release %u32_1 -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("no bits may be set for Memory Semantics of OpAtomic* " - "instructions")); -} - TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) { const std::string body = R"( OpAtomicStore %f32_1 %device %relaxed %f32_1 @@ -2032,75 +2009,6 @@ OpExtension "SPV_KHR_vulkan_memory_model" EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } -TEST_F(ValidateAtomics, WebGPUCrossDeviceMemoryScopeBad) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %cross_device %relaxed -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("in WebGPU environment Memory Scope is limited to " - "QueueFamilyKHR for OpAtomic* operations")); -} - -TEST_F(ValidateAtomics, WebGPUDeviceMemoryScopeBad) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("in WebGPU environment Memory Scope is limited to " - "QueueFamilyKHR for OpAtomic* operations")); -} - -TEST_F(ValidateAtomics, WebGPUWorkgroupMemoryScopeBad) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %workgroup %relaxed -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("in WebGPU environment Memory Scope is limited to " - "QueueFamilyKHR for OpAtomic* operations")); -} - -TEST_F(ValidateAtomics, WebGPUSubgroupMemoryScopeBad) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %subgroup %relaxed -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("in WebGPU environment Memory Scope is limited to " - "QueueFamilyKHR for OpAtomic* operations")); -} - -TEST_F(ValidateAtomics, WebGPUInvocationMemoryScopeBad) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %invocation %relaxed -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("in WebGPU environment Memory Scope is limited to " - "QueueFamilyKHR for OpAtomic* operations")); -} - -TEST_F(ValidateAtomics, WebGPUQueueFamilyMemoryScopeGood) { - const std::string body = R"( -%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %relaxed -)"; - - CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - TEST_F(ValidateAtomics, CompareExchangeWeakV13ValV14Good) { const std::string body = R"( %val1 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0 diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp index 8bd10d42..46f5b5a1 100644 --- a/test/val/val_barriers_test.cpp +++ b/test/val/val_barriers_test.cpp @@ -121,40 +121,13 @@ OpCapability Int64 execution_model, memory_model); } -std::string GenerateWebGPUComputeShaderCode( - const std::string& body, - const std::string& capabilities_and_extensions = "", - const std::string& execution_model = "GLCompute") { - const std::string vulkan_memory_capability = R"( -OpCapability VulkanMemoryModelKHR -)"; - const std::string vulkan_memory_extension = R"( -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - const std::string memory_model = "OpMemoryModel Logical VulkanKHR"; - return GenerateShaderCodeImpl(body, - vulkan_memory_capability + - capabilities_and_extensions + - vulkan_memory_extension, - "", execution_model, memory_model); -} - -std::string GenerateWebGPUVertexShaderCode( +std::string GenerateVulkanVertexShaderCode( const std::string& body, const std::string& capabilities_and_extensions = "", const std::string& execution_model = "Vertex") { - const std::string vulkan_memory_capability = R"( -OpCapability VulkanMemoryModelKHR -)"; - const std::string vulkan_memory_extension = R"( -OpExtension "SPV_KHR_vulkan_memory_model" -)"; - const std::string memory_model = "OpMemoryModel Logical VulkanKHR"; - return GenerateShaderCodeImpl(body, - vulkan_memory_capability + - capabilities_and_extensions + - vulkan_memory_extension, - "", execution_model, memory_model); + const std::string memory_model = "OpMemoryModel Logical GLSL450"; + return GenerateShaderCodeImpl(body, capabilities_and_extensions, "", + execution_model, memory_model); } std::string GenerateKernelCode( @@ -271,64 +244,6 @@ OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } -TEST_F(ValidateBarriers, OpControlBarrierWebGPUAcquireReleaseSuccess) { - const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire_release_workgroup -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateBarriers, OpControlBarrierWebGPURelaxedFailure) { - const std::string body = R"( -OpControlBarrier %workgroup %workgroup %workgroup -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, AcquireRelease must be set for Memory " - "Semantics of OpControlBarrier")); -} - -TEST_F(ValidateBarriers, OpControlBarrierWebGPUMissingWorkgroupFailure) { - const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire_release -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, WorkgroupMemory must be set for Memory " - "Semantics")); -} - -TEST_F(ValidateBarriers, OpControlBarrierWebGPUUniformFailure) { - const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("For WebGPU only WorkgroupMemory and AcquireRelease may be set " - "for Memory Semantics of OpControlBarrier.")); -} - -TEST_F(ValidateBarriers, OpControlBarrierWebGPUReleaseFailure) { - const std::string body = R"( -OpControlBarrier %workgroup %workgroup %release_uniform_workgroup -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, AcquireRelease must be set for Memory " - "Semantics of OpControlBarrier")); -} - TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv12) { const std::string body = R"( OpControlBarrier %device %device %none @@ -435,44 +350,6 @@ OpControlBarrier %device %workgroup %none "is limited to Workgroup and Subgroup")); } -TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeDeviceBad) { - const std::string body = R"( -OpControlBarrier %device %workgroup %none -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("ControlBarrier: in WebGPU environment Execution Scope " - "is limited to Workgroup")); -} - -TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeSubgroupBad) { - const std::string body = R"( -OpControlBarrier %subgroup %workgroup %none -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("ControlBarrier: in WebGPU environment Execution Scope " - "is limited to Workgroup")); -} - -TEST_F(ValidateBarriers, - OpControlBarrierWebGPUExecutionScopeWorkgroupNonComputeBad) { - const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire_release_workgroup -)"; - - CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Workgroup Execution Scope is limited to GLCompute execution model")); -} - TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroup) { const std::string body = R"( OpControlBarrier %subgroup %subgroup %none @@ -480,6 +357,8 @@ OpControlBarrier %subgroup %subgroup %none CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04638")); EXPECT_THAT( getDiagnosticString(), HasSubstr("ControlBarrier: in Vulkan 1.0 environment Memory Scope is " @@ -503,20 +382,33 @@ OpControlBarrier %subgroup %cross_device %none CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04638")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: in Vulkan environment, Memory Scope " "cannot be CrossDevice")); } -TEST_F(ValidateBarriers, OpControlBarrierWebGPUMemoryScopeNonWorkgroup) { +TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeFailure) { const std::string body = R"( -OpControlBarrier %workgroup %subgroup %acquire_release_workgroup +OpControlBarrier %workgroup %workgroup %acquire )"; - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ControlBarrier: in WebGPU environment Memory Scope is " - "limited to Workgroup for OpControlBarrier")); + AnyVUID("VUID-StandaloneSpirv-None-04639")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Workgroup Memory Scope is limited to MeshNV, TaskNV, " + "and GLCompute execution model")); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) { @@ -738,100 +630,6 @@ OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } -TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUImageMemorySuccess) { - const std::string body = R"( -OpMemoryBarrier %workgroup %image_memory -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUDeviceFailure) { - const std::string body = R"( -OpMemoryBarrier %subgroup %image_memory -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("in WebGPU environment Memory Scope is limited to " - "Workgroup for OpMemoryBarrier")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireReleaseFailure) { - const std::string body = R"( -OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("ImageMemory must be set for Memory Semantics of " - "OpMemoryBarrier")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierWebGPURelaxedFailure) { - const std::string body = R"( -OpMemoryBarrier %workgroup %uniform_workgroup -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("ImageMemory must be set for Memory Semantics of " - "OpMemoryBarrier")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireFailure) { - const std::string body = R"( -OpMemoryBarrier %workgroup %acquire_uniform_workgroup -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("ImageMemory must be set for Memory Semantics of " - "OpMemoryBarrier")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUReleaseFailure) { - const std::string body = R"( -OpMemoryBarrier %workgroup %release_uniform_workgroup -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("ImageMemory must be set for Memory Semantics of " - "OpMemoryBarrier")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUUniformFailure) { - const std::string body = R"( -OpMemoryBarrier %workgroup %uniform_image_memory -)"; - - CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("only ImageMemory may be set for Memory Semantics of " - "OpMemoryBarrier")); -} - -TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUWorkgroupNonComputeFailure) { - const std::string body = R"( -OpMemoryBarrier %workgroup %image_memory -)"; - - CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "Workgroup Memory Scope is limited to GLCompute execution model")); -} - TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) { const std::string body = R"( OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup @@ -885,6 +683,8 @@ OpMemoryBarrier %subgroup %acquire_release_uniform_workgroup CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04638")); EXPECT_THAT( getDiagnosticString(), HasSubstr("MemoryBarrier: in Vulkan 1.0 environment Memory Scope is " @@ -920,6 +720,8 @@ OpMemoryBarrier %device %none CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04732")); EXPECT_THAT( getDiagnosticString(), HasSubstr("MemoryBarrier: Vulkan specification requires Memory Semantics " @@ -935,6 +737,8 @@ OpMemoryBarrier %device %acquire CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04733")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryBarrier: expected Memory Semantics to include a " "Vulkan-supported storage class")); } @@ -947,6 +751,8 @@ OpMemoryBarrier %device %acquire_release_subgroup CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04733")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryBarrier: expected Memory Semantics to include a " "Vulkan-supported storage class")); } @@ -1629,6 +1435,8 @@ TEST_F(ValidateBarriers, OpMemoryBarrierShaderCallComputeFailure) { SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04640")); EXPECT_THAT( getDiagnosticString(), HasSubstr( diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index 936279a2..bbcdbb11 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp @@ -54,18 +54,14 @@ using ::testing::Values; using ::testing::ValuesIn; using ValidateBuiltIns = spvtest::ValidateBase<bool>; -using ValidateVulkanSubgroupBuiltIns = spvtest::ValidateBase< - std::tuple<const char*, const char*, const char*, const char*, TestResult>>; -using ValidateVulkanCombineBuiltInExecutionModelDataTypeResult = +using ValidateVulkanSubgroupBuiltIns = spvtest::ValidateBase<std::tuple<const char*, const char*, const char*, const char*, const char*, TestResult>>; -using ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult = +using ValidateVulkanCombineBuiltInExecutionModelDataTypeResult = spvtest::ValidateBase<std::tuple<const char*, const char*, const char*, - const char*, TestResult>>; + const char*, const char*, TestResult>>; using ValidateVulkanCombineBuiltInArrayedVariable = spvtest::ValidateBase< std::tuple<const char*, const char*, const char*, const char*, TestResult>>; -using ValidateWebGPUCombineBuiltInArrayedVariable = spvtest::ValidateBase< - std::tuple<const char*, const char*, const char*, const char*, TestResult>>; using ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult = spvtest::ValidateBase< std::tuple<const char*, const char*, const char*, const char*, @@ -76,22 +72,19 @@ using ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResu const char*, const char*, const char*, const char*, const char*, TestResult>>; -bool InitializerRequired(spv_target_env env, const char* const storage_class) { - return spvIsWebGPUEnv(env) && (strncmp(storage_class, "Output", 6) == 0 || - strncmp(storage_class, "Private", 7) == 0 || - strncmp(storage_class, "Function", 8) == 0); +bool InitializerRequired(const char* const storage_class) { + return (strncmp(storage_class, "Output", 6) == 0 || + strncmp(storage_class, "Private", 7) == 0 || + strncmp(storage_class, "Function", 8) == 0); } -CodeGenerator GetInMainCodeGenerator(spv_target_env env, - const char* const built_in, +CodeGenerator GetInMainCodeGenerator(const char* const built_in, const char* const execution_model, const char* const storage_class, const char* const capabilities, const char* const extensions, const char* const data_type) { - CodeGenerator generator = - spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); if (capabilities) { generator.capabilities_ += capabilities; @@ -107,13 +100,13 @@ CodeGenerator GetInMainCodeGenerator(spv_target_env env, std::ostringstream after_types; after_types << "%built_in_type = OpTypeStruct " << data_type << "\n"; - if (InitializerRequired(env, storage_class)) { + if (InitializerRequired(storage_class)) { after_types << "%built_in_null = OpConstantNull %built_in_type\n"; } after_types << "%built_in_ptr = OpTypePointer " << storage_class << " %built_in_type\n"; after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; - if (InitializerRequired(env, storage_class)) { + if (InitializerRequired(storage_class)) { after_types << " %built_in_null"; } after_types << "\n"; @@ -158,44 +151,6 @@ CodeGenerator GetInMainCodeGenerator(spv_target_env env, return generator; } -// Allows test parameter test to list all possible VUIDs with a delimiter that -// is then split here to check if one VUID was in the error message -MATCHER_P(AnyVUID, vuid_set, "VUID from the set is in error message") { - // use space as delimiter because clang-format will properly line break VUID - // strings which is important the entire VUID is in a single line for script - // to scan - std::string delimiter = " "; - std::string token; - std::string vuids = std::string(vuid_set); - size_t position; - - // Catch case were someone accidentally left spaces by trimming string - // clang-format off - vuids.erase(std::find_if(vuids.rbegin(), vuids.rend(), [](unsigned char c) { - return (c != ' '); - }).base(), vuids.end()); - vuids.erase(vuids.begin(), std::find_if(vuids.begin(), vuids.end(), [](unsigned char c) { - return (c != ' '); - })); - // clang-format on - - do { - position = vuids.find(delimiter); - if (position != std::string::npos) { - token = vuids.substr(0, position); - vuids.erase(0, position + delimiter.length()); - } else { - token = vuids.substr(0); // last item - } - - // arg contains diagnostic message - if (arg.find(token) != std::string::npos) { - return true; - } - } while (position != std::string::npos); - return false; -} - TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) { const char* const built_in = std::get<0>(GetParam()); const char* const execution_model = std::get<1>(GetParam()); @@ -204,9 +159,8 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) { const char* const vuid = std::get<4>(GetParam()); const TestResult& test_result = std::get<5>(GetParam()); - CodeGenerator generator = - GetInMainCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model, - storage_class, NULL, NULL, data_type); + CodeGenerator generator = GetInMainCodeGenerator( + built_in, execution_model, storage_class, NULL, NULL, data_type); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(test_result.validation_result, @@ -222,28 +176,6 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) { } } -TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InMain) { - const char* const built_in = std::get<0>(GetParam()); - const char* const execution_model = std::get<1>(GetParam()); - const char* const storage_class = std::get<2>(GetParam()); - const char* const data_type = std::get<3>(GetParam()); - const TestResult& test_result = std::get<4>(GetParam()); - - CodeGenerator generator = - GetInMainCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model, - storage_class, NULL, NULL, data_type); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(test_result.validation_result, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - if (test_result.error_str) { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); - } - if (test_result.error_str2) { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); - } -} - TEST_P( ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, InMain) { @@ -256,9 +188,9 @@ TEST_P( const char* const vuid = std::get<6>(GetParam()); const TestResult& test_result = std::get<7>(GetParam()); - CodeGenerator generator = GetInMainCodeGenerator( - SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, - capabilities, extensions, data_type); + CodeGenerator generator = + GetInMainCodeGenerator(built_in, execution_model, storage_class, + capabilities, extensions, data_type); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(test_result.validation_result, @@ -288,7 +220,7 @@ TEST_P( const TestResult& test_result = std::get<8>(GetParam()); CodeGenerator generator = - GetInMainCodeGenerator(env, built_in, execution_model, storage_class, + GetInMainCodeGenerator(built_in, execution_model, storage_class, capabilities, extensions, data_type); CompileSuccessfully(generator.Build(), env); @@ -304,16 +236,13 @@ TEST_P( } } -CodeGenerator GetInFunctionCodeGenerator(spv_target_env env, - const char* const built_in, +CodeGenerator GetInFunctionCodeGenerator(const char* const built_in, const char* const execution_model, const char* const storage_class, const char* const capabilities, const char* const extensions, const char* const data_type) { - CodeGenerator generator = - spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); if (capabilities) { generator.capabilities_ += capabilities; @@ -328,13 +257,13 @@ CodeGenerator GetInFunctionCodeGenerator(spv_target_env env, std::ostringstream after_types; after_types << "%built_in_type = OpTypeStruct " << data_type << "\n"; - if (InitializerRequired(env, storage_class)) { + if (InitializerRequired(storage_class)) { after_types << "%built_in_null = OpConstantNull %built_in_type\n"; } after_types << "%built_in_ptr = OpTypePointer " << storage_class << " %built_in_type\n"; after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; - if (InitializerRequired(env, storage_class)) { + if (InitializerRequired(storage_class)) { after_types << " %built_in_null"; } after_types << "\n"; @@ -383,11 +312,7 @@ OpReturn OpFunctionEnd )"; - if (spvIsWebGPUEnv(env)) { - generator.after_types_ += function_body; - } else { - generator.add_at_the_end_ = function_body; - } + generator.add_at_the_end_ = function_body; generator.entry_points_.push_back(std::move(entry_point)); @@ -402,9 +327,8 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InFunction) { const char* const vuid = std::get<4>(GetParam()); const TestResult& test_result = std::get<5>(GetParam()); - CodeGenerator generator = - GetInFunctionCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model, - storage_class, NULL, NULL, data_type); + CodeGenerator generator = GetInFunctionCodeGenerator( + built_in, execution_model, storage_class, NULL, NULL, data_type); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(test_result.validation_result, @@ -420,28 +344,6 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InFunction) { } } -TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InFunction) { - const char* const built_in = std::get<0>(GetParam()); - const char* const execution_model = std::get<1>(GetParam()); - const char* const storage_class = std::get<2>(GetParam()); - const char* const data_type = std::get<3>(GetParam()); - const TestResult& test_result = std::get<4>(GetParam()); - - CodeGenerator generator = - GetInFunctionCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model, - storage_class, NULL, NULL, data_type); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(test_result.validation_result, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - if (test_result.error_str) { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); - } - if (test_result.error_str2) { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); - } -} - TEST_P( ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, InFunction) { @@ -454,9 +356,9 @@ TEST_P( const char* const vuid = std::get<6>(GetParam()); const TestResult& test_result = std::get<7>(GetParam()); - CodeGenerator generator = GetInFunctionCodeGenerator( - SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, - capabilities, extensions, data_type); + CodeGenerator generator = + GetInFunctionCodeGenerator(built_in, execution_model, storage_class, + capabilities, extensions, data_type); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(test_result.validation_result, @@ -472,16 +374,13 @@ TEST_P( } } -CodeGenerator GetVariableCodeGenerator(spv_target_env env, - const char* const built_in, +CodeGenerator GetVariableCodeGenerator(const char* const built_in, const char* const execution_model, const char* const storage_class, const char* const capabilities, const char* const extensions, const char* const data_type) { - CodeGenerator generator = - spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); if (capabilities) { generator.capabilities_ += capabilities; @@ -495,13 +394,13 @@ CodeGenerator GetVariableCodeGenerator(spv_target_env env, generator.before_types_ += "\n"; std::ostringstream after_types; - if (InitializerRequired(env, storage_class)) { + if (InitializerRequired(storage_class)) { after_types << "%built_in_null = OpConstantNull " << data_type << "\n"; } after_types << "%built_in_ptr = OpTypePointer " << storage_class << " " << data_type << "\n"; after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; - if (InitializerRequired(env, storage_class)) { + if (InitializerRequired(storage_class)) { after_types << " %built_in_null"; } after_types << "\n"; @@ -553,9 +452,8 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) { const char* const vuid = std::get<4>(GetParam()); const TestResult& test_result = std::get<5>(GetParam()); - CodeGenerator generator = - GetVariableCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model, - storage_class, NULL, NULL, data_type); + CodeGenerator generator = GetVariableCodeGenerator( + built_in, execution_model, storage_class, NULL, NULL, data_type); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(test_result.validation_result, @@ -571,28 +469,6 @@ TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) { } } -TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, Variable) { - const char* const built_in = std::get<0>(GetParam()); - const char* const execution_model = std::get<1>(GetParam()); - const char* const storage_class = std::get<2>(GetParam()); - const char* const data_type = std::get<3>(GetParam()); - const TestResult& test_result = std::get<4>(GetParam()); - - CodeGenerator generator = - GetVariableCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model, - storage_class, NULL, NULL, data_type); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(test_result.validation_result, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - if (test_result.error_str) { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); - } - if (test_result.error_str2) { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); - } -} - TEST_P( ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, Variable) { @@ -605,9 +481,9 @@ TEST_P( const char* const vuid = std::get<6>(GetParam()); const TestResult& test_result = std::get<7>(GetParam()); - CodeGenerator generator = GetVariableCodeGenerator( - SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, - capabilities, extensions, data_type); + CodeGenerator generator = + GetVariableCodeGenerator(built_in, execution_model, storage_class, + capabilities, extensions, data_type); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(test_result.validation_result, @@ -642,10 +518,26 @@ INSTANTIATE_TEST_SUITE_P( Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceInvalidStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), + Values("Vertex", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Private"), Values("%f32arr2", "%f32arr4"), + Values("VUID-ClipDistance-ClipDistance-04190 " + "VUID-CullDistance-CullDistance-04199"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input or Output storage " + "class.")))); + +INSTANTIATE_TEST_SUITE_P( ClipAndCullDistanceFragmentOutput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("ClipDistance", "CullDistance"), Values("Fragment"), - Values("Output"), Values("%f32arr4"), Values(nullptr), + Values("Output"), Values("%f32arr4"), + Values("VUID-ClipDistance-ClipDistance-04189 " + "VUID-CullDistance-CullDistance-04198"), Values(TestResult( SPV_ERROR_INVALID_DATA, "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance " @@ -667,7 +559,9 @@ INSTANTIATE_TEST_SUITE_P( ClipAndCullDistanceVertexInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("ClipDistance", "CullDistance"), Values("Vertex"), - Values("Input"), Values("%f32arr4"), Values(nullptr), + Values("Input"), Values("%f32arr4"), + Values("VUID-ClipDistance-ClipDistance-04188 " + "VUID-CullDistance-CullDistance-04197"), Values(TestResult( SPV_ERROR_INVALID_DATA, "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance " @@ -726,11 +620,6 @@ INSTANTIATE_TEST_SUITE_P( Values("%f32vec4"), Values(nullptr), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - FragCoordSuccess, ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), - Values("%f32vec4"), Values(TestResult()))); - -INSTANTIATE_TEST_SUITE_P( FragCoordNotFragment, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine( @@ -743,15 +632,6 @@ INSTANTIATE_TEST_SUITE_P( "to be used only with Fragment execution model")))); INSTANTIATE_TEST_SUITE_P( - FragCoordNotFragment, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("FragCoord"), Values("Vertex", "GLCompute"), Values("Input"), - Values("%f32vec4"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with Fragment execution model")))); - -INSTANTIATE_TEST_SUITE_P( FragCoordNotInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FragCoord"), Values("Fragment"), Values("Output"), Values("%f32vec4"), Values("VUID-FragCoord-FragCoord-04211"), @@ -761,15 +641,6 @@ INSTANTIATE_TEST_SUITE_P( "uses storage class Output")))); INSTANTIATE_TEST_SUITE_P( - FragCoordNotInput, ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FragCoord"), Values("Fragment"), Values("Output"), - Values("%f32vec4"), - Values(TestResult( - SPV_ERROR_INVALID_DATA, - "to be only used for variables with Input storage class", - "uses storage class Output")))); - -INSTANTIATE_TEST_SUITE_P( FragCoordNotFloatVector, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), @@ -780,15 +651,6 @@ INSTANTIATE_TEST_SUITE_P( "is not a float vector")))); INSTANTIATE_TEST_SUITE_P( - FragCoordNotFloatVector, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), - Values("%f32arr4", "%u32vec4"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 4-component 32-bit float vector", - "is not a float vector")))); - -INSTANTIATE_TEST_SUITE_P( FragCoordNotFloatVec4, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), @@ -798,15 +660,6 @@ INSTANTIATE_TEST_SUITE_P( "has 3 components")))); INSTANTIATE_TEST_SUITE_P( - FragCoordNotFloatVec4, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), - Values("%f32vec3"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 4-component 32-bit float vector", - "has 3 components")))); - -INSTANTIATE_TEST_SUITE_P( FragCoordNotF32Vec4, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), @@ -821,11 +674,6 @@ INSTANTIATE_TEST_SUITE_P( Values("%f32"), Values(nullptr), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - FragDepthSuccess, ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), - Values("%f32"), Values(TestResult()))); - -INSTANTIATE_TEST_SUITE_P( FragDepthNotFragment, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine( @@ -838,15 +686,6 @@ INSTANTIATE_TEST_SUITE_P( "to be used only with Fragment execution model")))); INSTANTIATE_TEST_SUITE_P( - FragDepthNotFragment, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("FragDepth"), Values("Vertex", "GLCompute"), Values("Output"), - Values("%f32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with Fragment execution model")))); - -INSTANTIATE_TEST_SUITE_P( FragDepthNotOutput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FragDepth"), Values("Fragment"), Values("Input"), @@ -857,16 +696,6 @@ INSTANTIATE_TEST_SUITE_P( "uses storage class Input")))); INSTANTIATE_TEST_SUITE_P( - FragDepthNotOutput, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FragDepth"), Values("Fragment"), Values("Input"), - Values("%f32"), - Values(TestResult( - SPV_ERROR_INVALID_DATA, - "to be only used for variables with Output storage class", - "uses storage class Input")))); - -INSTANTIATE_TEST_SUITE_P( FragDepthNotFloatScalar, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), @@ -877,15 +706,6 @@ INSTANTIATE_TEST_SUITE_P( "is not a float scalar")))); INSTANTIATE_TEST_SUITE_P( - FragDepthNotFloatScalar, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), - Values("%f32vec4", "%u32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 32-bit float scalar", - "is not a float scalar")))); - -INSTANTIATE_TEST_SUITE_P( FragDepthNotF32, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), Values("%f64"), Values("VUID-FragDepth-FragDepth-04215"), @@ -901,12 +721,6 @@ INSTANTIATE_TEST_SUITE_P( Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - FrontFacingSuccess, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FrontFacing"), Values("Fragment"), Values("Input"), - Values("%bool"), Values(TestResult()))); - -INSTANTIATE_TEST_SUITE_P( FrontFacingAndHelperInvocationNotFragment, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine( @@ -920,15 +734,6 @@ INSTANTIATE_TEST_SUITE_P( "to be used only with Fragment execution model")))); INSTANTIATE_TEST_SUITE_P( - FrontFacingNotFragment, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("FrontFacing"), Values("Vertex", "GLCompute"), Values("Input"), - Values("%bool"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with Fragment execution model")))); - -INSTANTIATE_TEST_SUITE_P( FrontFacingAndHelperInvocationNotInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FrontFacing", "HelperInvocation"), Values("Fragment"), @@ -941,16 +746,6 @@ INSTANTIATE_TEST_SUITE_P( "uses storage class Output")))); INSTANTIATE_TEST_SUITE_P( - FrontFacingNotInput, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FrontFacing"), Values("Fragment"), Values("Output"), - Values("%bool"), - Values(TestResult( - SPV_ERROR_INVALID_DATA, - "to be only used for variables with Input storage class", - "uses storage class Output")))); - -INSTANTIATE_TEST_SUITE_P( FrontFacingAndHelperInvocationNotBool, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("FrontFacing", "HelperInvocation"), Values("Fragment"), @@ -962,15 +757,6 @@ INSTANTIATE_TEST_SUITE_P( "is not a bool scalar")))); INSTANTIATE_TEST_SUITE_P( - FrontFacingNotBool, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("FrontFacing"), Values("Fragment"), Values("Input"), - Values("%f32", "%u32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a bool scalar", - "is not a bool scalar")))); - -INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3Success, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", @@ -979,13 +765,6 @@ INSTANTIATE_TEST_SUITE_P( Values(nullptr), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - ComputeShaderInputInt32Vec3Success, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), - Values("GLCompute"), Values("Input"), Values("%u32vec3"), - Values(TestResult()))); - -INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotGLCompute, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine( @@ -1002,15 +781,6 @@ INSTANTIATE_TEST_SUITE_P( "to be used only with GLCompute execution model")))); INSTANTIATE_TEST_SUITE_P( - ComputeShaderInputInt32Vec3NotGLCompute, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), - Values("Vertex", "Fragment"), Values("Input"), Values("%u32vec3"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with GLCompute execution model")))); - -INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", @@ -1026,16 +796,6 @@ INSTANTIATE_TEST_SUITE_P( "uses storage class Output")))); INSTANTIATE_TEST_SUITE_P( - ComputeShaderInputInt32Vec3NotInput, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), - Values("GLCompute"), Values("Output"), Values("%u32vec3"), - Values(TestResult( - SPV_ERROR_INVALID_DATA, - "to be only used for variables with Input storage class", - "uses storage class Output")))); - -INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotIntVector, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", @@ -1051,16 +811,6 @@ INSTANTIATE_TEST_SUITE_P( "is not an int vector")))); INSTANTIATE_TEST_SUITE_P( - ComputeShaderInputInt32Vec3NotIntVector, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), - Values("GLCompute"), Values("Input"), - Values("%u32arr3", "%f32vec3"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 3-component 32-bit int vector", - "is not an int vector")))); - -INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotIntVec3, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", @@ -1075,15 +825,6 @@ INSTANTIATE_TEST_SUITE_P( "has 4 components")))); INSTANTIATE_TEST_SUITE_P( - ComputeShaderInputInt32Vec3NotIntVec3, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), - Values("GLCompute"), Values("Input"), Values("%u32vec4"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 3-component 32-bit int vector", - "has 4 components")))); - -INSTANTIATE_TEST_SUITE_P( ComputeShaderInputInt32Vec3NotInt32Vec, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", @@ -1153,12 +894,6 @@ INSTANTIATE_TEST_SUITE_P( Values("%u32"), Values(nullptr), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - InstanceIndexSuccess, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), - Values("%u32"), Values(TestResult()))); - -INSTANTIATE_TEST_SUITE_P( InstanceIndexInvalidExecutionModel, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("InstanceIndex"), @@ -1170,14 +905,6 @@ INSTANTIATE_TEST_SUITE_P( "to be used only with Vertex execution model")))); INSTANTIATE_TEST_SUITE_P( - InstanceIndexInvalidExecutionModel, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("InstanceIndex"), Values("Fragment", "GLCompute"), - Values("Input"), Values("%u32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with Vertex execution model")))); - -INSTANTIATE_TEST_SUITE_P( InstanceIndexNotInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("InstanceIndex"), Values("Vertex"), Values("Output"), @@ -1188,16 +915,6 @@ INSTANTIATE_TEST_SUITE_P( "uses storage class Output")))); INSTANTIATE_TEST_SUITE_P( - InstanceIndexNotInput, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("InstanceIndex"), Values("Vertex"), Values("Output"), - Values("%u32"), - Values(TestResult( - SPV_ERROR_INVALID_DATA, - "to be only used for variables with Input storage class", - "uses storage class Output")))); - -INSTANTIATE_TEST_SUITE_P( InstanceIndexNotIntScalar, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), @@ -1208,15 +925,6 @@ INSTANTIATE_TEST_SUITE_P( "is not an int scalar")))); INSTANTIATE_TEST_SUITE_P( - InstanceIndexNotIntScalar, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), - Values("%f32", "%u32vec3"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 32-bit int scalar", - "is not an int scalar")))); - -INSTANTIATE_TEST_SUITE_P( InstanceIndexNotInt32, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), @@ -1256,7 +964,8 @@ INSTANTIATE_TEST_SUITE_P( ViewportIndexExecutionModelEnabledByCapability, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("ViewportIndex"), Values("Vertex", "TessellationEvaluation"), - Values("Output"), Values("%u32"), Values(nullptr), + Values("Output"), Values("%u32"), + Values("VUID-ViewportIndex-ViewportIndex-04405"), Values(TestResult( SPV_ERROR_INVALID_DATA, "ShaderViewportIndexLayerEXT or ShaderViewportIndex")))); @@ -1265,7 +974,7 @@ INSTANTIATE_TEST_SUITE_P( LayerExecutionModelEnabledByCapability, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("Layer"), Values("Vertex", "TessellationEvaluation"), - Values("Output"), Values("%u32"), Values(nullptr), + Values("Output"), Values("%u32"), Values("VUID-Layer-Layer-04273"), Values(TestResult(SPV_ERROR_INVALID_DATA, "ShaderViewportIndexLayerEXT or ShaderLayer")))); @@ -1507,21 +1216,6 @@ INSTANTIATE_TEST_SUITE_P( Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - PositionOutputSuccess, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("Position"), Values("Vertex"), Values("Output"), - Values("%f32vec4"), Values(TestResult()))); - -INSTANTIATE_TEST_SUITE_P( - PositionOutputFailure, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("Position"), Values("Fragment", "GLCompute"), - Values("Output"), Values("%f32vec4"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "WebGPU spec allows BuiltIn Position to be used " - "only with the Vertex execution model.")))); - -INSTANTIATE_TEST_SUITE_P( PositionInputSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("Position"), @@ -1530,20 +1224,22 @@ INSTANTIATE_TEST_SUITE_P( Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - PositionInputFailure, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("Position"), Values("Vertex", "Fragment", "GLCompute"), - Values("Input"), Values("%f32vec4"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "WebGPU spec allows BuiltIn Position to be only used " - "for variables with Output storage class")))); + PositionInvalidStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), + Values("Geometry", "TessellationControl", "TessellationEvaluation"), + Values("Private"), Values("%f32vec4"), + Values("VUID-Position-Position-04320"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn Position to be only used for " + "variables with Input or Output storage class.")))); INSTANTIATE_TEST_SUITE_P( PositionVertexInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("Position"), Values("Vertex"), Values("Input"), - Values("%f32vec4"), Values("VUID-Position-Position-04320"), + Values("%f32vec4"), Values("VUID-Position-Position-04319"), Values(TestResult( SPV_ERROR_INVALID_DATA, "Vulkan spec doesn't allow BuiltIn Position " @@ -1573,15 +1269,6 @@ INSTANTIATE_TEST_SUITE_P( "is not a float vector")))); INSTANTIATE_TEST_SUITE_P( - PositionNotFloatVector, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("Position"), Values("Vertex"), Values("Output"), - Values("%f32arr4", "%u32vec4"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 4-component 32-bit float vector")))); - -INSTANTIATE_TEST_SUITE_P( PositionNotFloatVec4, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("Position"), Values("Geometry"), Values("Input"), @@ -1591,15 +1278,6 @@ INSTANTIATE_TEST_SUITE_P( "has 3 components")))); INSTANTIATE_TEST_SUITE_P( - PositionNotFloatVec4, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("Position"), Values("Vertex"), Values("Output"), - Values("%f32vec3"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 4-component 32-bit float vector")))); - -INSTANTIATE_TEST_SUITE_P( PositionNotF32Vec4, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("Position"), Values("Geometry"), Values("Input"), @@ -1918,7 +1596,8 @@ INSTANTIATE_TEST_SUITE_P( TessLevelOuterOutputTese, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("TessLevelOuter"), Values("TessellationEvaluation"), - Values("Output"), Values("%f32arr4"), Values(nullptr), + Values("Output"), Values("%f32arr4"), + Values("VUID-TessLevelOuter-TessLevelOuter-04392"), Values(TestResult( SPV_ERROR_INVALID_DATA, "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " @@ -1929,7 +1608,8 @@ INSTANTIATE_TEST_SUITE_P( TessLevelOuterInputTesc, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("TessLevelOuter"), Values("TessellationControl"), - Values("Input"), Values("%f32arr4"), Values(nullptr), + Values("Input"), Values("%f32arr4"), + Values("VUID-TessLevelOuter-TessLevelOuter-04391"), Values(TestResult( SPV_ERROR_INVALID_DATA, "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " @@ -2005,7 +1685,8 @@ INSTANTIATE_TEST_SUITE_P( TessLevelInnerOutputTese, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("TessLevelInner"), Values("TessellationEvaluation"), - Values("Output"), Values("%f32arr2"), Values(nullptr), + Values("Output"), Values("%f32arr2"), + Values("VUID-TessLevelInner-TessLevelInner-04396"), Values(TestResult( SPV_ERROR_INVALID_DATA, "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " @@ -2016,7 +1697,8 @@ INSTANTIATE_TEST_SUITE_P( TessLevelInnerInputTesc, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("TessLevelInner"), Values("TessellationControl"), - Values("Input"), Values("%f32arr2"), Values(nullptr), + Values("Input"), Values("%f32arr2"), + Values("VUID-TessLevelInner-TessLevelInner-04395"), Values(TestResult( SPV_ERROR_INVALID_DATA, "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " @@ -2070,12 +1752,6 @@ INSTANTIATE_TEST_SUITE_P( Values("%u32"), Values(nullptr), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - VertexIndexSuccess, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), - Values("%u32"), Values(TestResult()))); - -INSTANTIATE_TEST_SUITE_P( VertexIndexInvalidExecutionModel, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("VertexIndex"), @@ -2087,14 +1763,6 @@ INSTANTIATE_TEST_SUITE_P( "to be used only with Vertex execution model")))); INSTANTIATE_TEST_SUITE_P( - VertexIndexInvalidExecutionModel, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("VertexIndex"), Values("Fragment", "GLCompute"), - Values("Input"), Values("%u32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with Vertex execution model")))); - -INSTANTIATE_TEST_SUITE_P( VertexIndexNotInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine( @@ -2105,16 +1773,6 @@ INSTANTIATE_TEST_SUITE_P( "used for variables with Input storage class")))); INSTANTIATE_TEST_SUITE_P( - VertexIndexNotInput, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("VertexIndex"), Values("Vertex"), Values("Output"), - Values("%u32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "WebGPU spec allows BuiltIn VertexIndex to be only " - "used for variables with Input storage class")))); - -INSTANTIATE_TEST_SUITE_P( VertexIndexNotIntScalar, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), @@ -2125,15 +1783,6 @@ INSTANTIATE_TEST_SUITE_P( "is not an int scalar")))); INSTANTIATE_TEST_SUITE_P( - VertexIndexNotIntScalar, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), - Values("%f32", "%u32vec3"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 32-bit int scalar", - "is not an int scalar")))); - -INSTANTIATE_TEST_SUITE_P( VertexIndexNotInt32, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), @@ -2143,50 +1792,6 @@ INSTANTIATE_TEST_SUITE_P( "has bit width 64")))); INSTANTIATE_TEST_SUITE_P( - LocalInvocationIndexSuccess, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("LocalInvocationIndex"), Values("GLCompute"), - Values("Input"), Values("%u32"), Values(TestResult()))); - -INSTANTIATE_TEST_SUITE_P( - LocalInvocationIndexInvalidExecutionModel, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("LocalInvocationIndex"), Values("Fragment", "Vertex"), - Values("Input"), Values("%u32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "to be used only with GLCompute execution model")))); - -INSTANTIATE_TEST_SUITE_P( - LocalInvocationIndexNotInput, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine( - Values("LocalInvocationIndex"), Values("GLCompute"), Values("Output"), - Values("%u32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "WebGPU spec allows BuiltIn LocalInvocationIndex to " - "be only used for variables with Input storage " - "class")))); - -INSTANTIATE_TEST_SUITE_P( - LocalInvocationIndexNotIntScalar, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("LocalInvocationIndex"), Values("GLCompute"), - Values("Input"), Values("%f32", "%u32vec3"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 32-bit int", "is not an int")))); - -INSTANTIATE_TEST_SUITE_P( - AllowListRejection, - ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, - Combine(Values("PointSize", "ClipDistance", "CullDistance", "VertexId", - "InstanceId", "PointCoord", "SampleMask", "HelperInvocation", - "WorkgroupId"), - Values("Vertex"), Values("Input"), Values("%u32"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "WebGPU does not allow BuiltIn")))); - -INSTANTIATE_TEST_SUITE_P( BaseInstanceOrVertexSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, Combine(Values("BaseInstance", "BaseVertex"), Values("Vertex"), @@ -2938,14 +2543,11 @@ INSTANTIATE_TEST_SUITE_P( Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 3-component 32-bit int vector")))); -CodeGenerator GetArrayedVariableCodeGenerator(spv_target_env env, - const char* const built_in, +CodeGenerator GetArrayedVariableCodeGenerator(const char* const built_in, const char* const execution_model, const char* const storage_class, const char* const data_type) { - CodeGenerator generator = - spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = "OpDecorate %built_in_var BuiltIn "; generator.before_types_ += built_in; @@ -2953,14 +2555,14 @@ CodeGenerator GetArrayedVariableCodeGenerator(spv_target_env env, std::ostringstream after_types; after_types << "%built_in_array = OpTypeArray " << data_type << " %u32_3\n"; - if (InitializerRequired(env, storage_class)) { + if (InitializerRequired(storage_class)) { after_types << "%built_in_array_null = OpConstantNull %built_in_array\n"; } after_types << "%built_in_ptr = OpTypePointer " << storage_class << " %built_in_array\n"; after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; - if (InitializerRequired(env, storage_class)) { + if (InitializerRequired(storage_class)) { after_types << " %built_in_array_null"; } after_types << "\n"; @@ -3009,7 +2611,7 @@ TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) { const TestResult& test_result = std::get<4>(GetParam()); CodeGenerator generator = GetArrayedVariableCodeGenerator( - SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type); + built_in, execution_model, storage_class, data_type); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(test_result.validation_result, @@ -3022,27 +2624,6 @@ TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) { } } -TEST_P(ValidateWebGPUCombineBuiltInArrayedVariable, Variable) { - const char* const built_in = std::get<0>(GetParam()); - const char* const execution_model = std::get<1>(GetParam()); - const char* const storage_class = std::get<2>(GetParam()); - const char* const data_type = std::get<3>(GetParam()); - const TestResult& test_result = std::get<4>(GetParam()); - - CodeGenerator generator = GetArrayedVariableCodeGenerator( - SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(test_result.validation_result, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - if (test_result.error_str) { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); - } - if (test_result.error_str2) { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); - } -} - INSTANTIATE_TEST_SUITE_P(PointSizeArrayedF32TessControl, ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("PointSize"), @@ -3089,14 +2670,6 @@ INSTANTIATE_TEST_SUITE_P( "is not a float vector")))); INSTANTIATE_TEST_SUITE_P( - PositionArrayedF32Vec4Vertex, ValidateWebGPUCombineBuiltInArrayedVariable, - Combine(Values("Position"), Values("Vertex"), Values("Output"), - Values("%f32vec4"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 4-component 32-bit float vector", - "is not a float vector")))); - -INSTANTIATE_TEST_SUITE_P( ClipAndCullDistanceOutputSuccess, ValidateVulkanCombineBuiltInArrayedVariable, Combine(Values("ClipDistance", "CullDistance"), @@ -3199,10 +2772,8 @@ INSTANTIATE_TEST_SUITE_P( "needs to be a 32-bit int scalar", "has bit width 64")))); -CodeGenerator GetWorkgroupSizeSuccessGenerator(spv_target_env env) { - CodeGenerator generator = - env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); +CodeGenerator GetWorkgroupSizeSuccessGenerator() { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( OpDecorate %workgroup_size BuiltIn WorkgroupSize @@ -3224,22 +2795,13 @@ OpDecorate %workgroup_size BuiltIn WorkgroupSize } TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeSuccess) { - CodeGenerator generator = - GetWorkgroupSizeSuccessGenerator(SPV_ENV_VULKAN_1_0); + CodeGenerator generator = GetWorkgroupSizeSuccessGenerator(); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } -TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeSuccess) { - CodeGenerator generator = GetWorkgroupSizeSuccessGenerator(SPV_ENV_WEBGPU_0); - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -CodeGenerator GetWorkgroupSizeFragmentGenerator(spv_target_env env) { - CodeGenerator generator = - env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); +CodeGenerator GetWorkgroupSizeFragmentGenerator() { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( OpDecorate %workgroup_size BuiltIn WorkgroupSize @@ -3262,8 +2824,7 @@ OpDecorate %workgroup_size BuiltIn WorkgroupSize } TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeFragment) { - CodeGenerator generator = - GetWorkgroupSizeFragmentGenerator(SPV_ENV_VULKAN_1_0); + CodeGenerator generator = GetWorkgroupSizeFragmentGenerator(); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); @@ -3279,20 +2840,6 @@ TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeFragment) { "VUID-WorkgroupSize-WorkgroupSize-04427")); } -TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeFragment) { - CodeGenerator generator = GetWorkgroupSizeFragmentGenerator(SPV_ENV_WEBGPU_0); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("WebGPU spec allows BuiltIn WorkgroupSize to be used " - "only with GLCompute execution model")); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("is referencing ID <2> (OpConstantComposite) which is " - "decorated with BuiltIn WorkgroupSize in function <1> " - "called with execution model Fragment")); -} - TEST_F(ValidateBuiltIns, WorkgroupSizeNotConstant) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( @@ -3318,10 +2865,8 @@ OpDecorate %copy BuiltIn WorkgroupSize HasSubstr("BuiltIns can only target variables, structs or constants")); } -CodeGenerator GetWorkgroupSizeNotVectorGenerator(spv_target_env env) { - CodeGenerator generator = - env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); +CodeGenerator GetWorkgroupSizeNotVectorGenerator() { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( OpDecorate %workgroup_size BuiltIn WorkgroupSize @@ -3343,8 +2888,7 @@ OpDecorate %workgroup_size BuiltIn WorkgroupSize } TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotVector) { - CodeGenerator generator = - GetWorkgroupSizeNotVectorGenerator(SPV_ENV_VULKAN_1_0); + CodeGenerator generator = GetWorkgroupSizeNotVectorGenerator(); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); @@ -3356,22 +2900,8 @@ TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotVector) { AnyVUID("VUID-WorkgroupSize-WorkgroupSize-04427")); } -TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeNotVector) { - CodeGenerator generator = - GetWorkgroupSizeNotVectorGenerator(SPV_ENV_WEBGPU_0); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("According to the WebGPU spec BuiltIn WorkgroupSize " - "variable needs to be a 3-component 32-bit int vector. " - "ID <2> (OpConstant) is not an int vector.")); -} - -CodeGenerator GetWorkgroupSizeNotIntVectorGenerator(spv_target_env env) { - CodeGenerator generator = - env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); +CodeGenerator GetWorkgroupSizeNotIntVectorGenerator() { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( OpDecorate %workgroup_size BuiltIn WorkgroupSize @@ -3393,8 +2923,7 @@ OpDecorate %workgroup_size BuiltIn WorkgroupSize } TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotIntVector) { - CodeGenerator generator = - GetWorkgroupSizeNotIntVectorGenerator(SPV_ENV_VULKAN_1_0); + CodeGenerator generator = GetWorkgroupSizeNotIntVectorGenerator(); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); @@ -3406,22 +2935,8 @@ TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotIntVector) { AnyVUID("VUID-WorkgroupSize-WorkgroupSize-04427")); } -TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeNotIntVector) { - CodeGenerator generator = - GetWorkgroupSizeNotIntVectorGenerator(SPV_ENV_WEBGPU_0); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("According to the WebGPU spec BuiltIn WorkgroupSize " - "variable needs to be a 3-component 32-bit int vector. " - "ID <2> (OpConstantComposite) is not an int vector.")); -} - -CodeGenerator GetWorkgroupSizeNotVec3Generator(spv_target_env env) { - CodeGenerator generator = - env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); +CodeGenerator GetWorkgroupSizeNotVec3Generator() { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( OpDecorate %workgroup_size BuiltIn WorkgroupSize @@ -3443,8 +2958,7 @@ OpDecorate %workgroup_size BuiltIn WorkgroupSize } TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotVec3) { - CodeGenerator generator = - GetWorkgroupSizeNotVec3Generator(SPV_ENV_VULKAN_1_0); + CodeGenerator generator = GetWorkgroupSizeNotVec3Generator(); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); @@ -3456,17 +2970,6 @@ TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotVec3) { AnyVUID("VUID-WorkgroupSize-WorkgroupSize-04427")); } -TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeNotVec3) { - CodeGenerator generator = GetWorkgroupSizeNotVec3Generator(SPV_ENV_WEBGPU_0); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("According to the WebGPU spec BuiltIn WorkgroupSize " - "variable needs to be a 3-component 32-bit int vector. " - "ID <2> (OpConstantComposite) has 2 components.")); -} - TEST_F(ValidateBuiltIns, WorkgroupSizeNotInt32Vec) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( @@ -3744,10 +3247,8 @@ OpFunctionEnd HasSubstr("called with execution model Fragment")); } -CodeGenerator GetNoDepthReplacingGenerator(spv_target_env env) { - CodeGenerator generator = - spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); +CodeGenerator GetNoDepthReplacingGenerator() { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( OpMemberDecorate %output_type 0 BuiltIn FragDepth @@ -3780,17 +3281,13 @@ OpReturn OpFunctionEnd )"; - if (spvIsWebGPUEnv(env)) { - generator.after_types_ += function_body; - } else { generator.add_at_the_end_ = function_body; - } return generator; } TEST_F(ValidateBuiltIns, VulkanFragmentFragDepthNoDepthReplacing) { - CodeGenerator generator = GetNoDepthReplacingGenerator(SPV_ENV_VULKAN_1_0); + CodeGenerator generator = GetNoDepthReplacingGenerator(); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); @@ -3801,21 +3298,8 @@ TEST_F(ValidateBuiltIns, VulkanFragmentFragDepthNoDepthReplacing) { HasSubstr("VUID-FragDepth-FragDepth-04216")); } -TEST_F(ValidateBuiltIns, WebGPUFragmentFragDepthNoDepthReplacing) { - CodeGenerator generator = GetNoDepthReplacingGenerator(SPV_ENV_WEBGPU_0); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("WebGPU spec requires DepthReplacing execution mode to " - "be declared when using BuiltIn FragDepth")); -} - -CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator( - spv_target_env env) { - CodeGenerator generator = - spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() - : CodeGenerator::GetDefaultShaderCodeGenerator(); +CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator() { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( OpMemberDecorate %output_type 0 BuiltIn FragDepth @@ -3859,19 +3343,14 @@ OpReturn OpFunctionEnd )"; - if (spvIsWebGPUEnv(env)) { - generator.after_types_ += function_body; - } else { generator.add_at_the_end_ = function_body; - } return generator; } TEST_F(ValidateBuiltIns, VulkanFragmentFragDepthOneMainHasDepthReplacingOtherHasnt) { - CodeGenerator generator = - GetOneMainHasDepthReplacingOtherHasntGenerator(SPV_ENV_VULKAN_1_0); + CodeGenerator generator = GetOneMainHasDepthReplacingOtherHasntGenerator(); CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); @@ -3882,17 +3361,6 @@ TEST_F(ValidateBuiltIns, HasSubstr("VUID-FragDepth-FragDepth-04216")); } -TEST_F(ValidateBuiltIns, - WebGPUFragmentFragDepthOneMainHasDepthReplacingOtherHasnt) { - CodeGenerator generator = - GetOneMainHasDepthReplacingOtherHasntGenerator(SPV_ENV_WEBGPU_0); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("WebGPU spec requires DepthReplacing execution mode to " - "be declared when using BuiltIn FragDepth")); -} TEST_F(ValidateBuiltIns, AllowInstanceIdWithIntersectionShader) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); @@ -4047,7 +3515,8 @@ TEST_P(ValidateVulkanSubgroupBuiltIns, InMain) { const char* const execution_model = std::get<1>(GetParam()); const char* const storage_class = std::get<2>(GetParam()); const char* const data_type = std::get<3>(GetParam()); - const TestResult& test_result = std::get<4>(GetParam()); + const char* const vuid = std::get<4>(GetParam()); + const TestResult& test_result = std::get<5>(GetParam()); CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.capabilities_ += R"( @@ -4107,6 +3576,9 @@ OpCapability GroupNonUniformBallot if (test_result.error_str2) { EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } } INSTANTIATE_TEST_SUITE_P( @@ -4114,6 +3586,11 @@ INSTANTIATE_TEST_SUITE_P( Combine(Values("SubgroupEqMask", "SubgroupGeMask", "SubgroupGtMask", "SubgroupLeMask", "SubgroupLtMask"), Values("GLCompute"), Values("Input"), Values("%u32vec3"), + Values("VUID-SubgroupEqMask-SubgroupEqMask-04371 " + "VUID-SubgroupGeMask-SubgroupGeMask-04373 " + "VUID-SubgroupGtMask-SubgroupGtMask-04375 " + "VUID-SubgroupLeMask-SubgroupLeMask-04377 " + "VUID-SubgroupLtMask-SubgroupLtMask-04379"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 4-component 32-bit int vector")))); @@ -4122,6 +3599,11 @@ INSTANTIATE_TEST_SUITE_P( Combine(Values("SubgroupEqMask", "SubgroupGeMask", "SubgroupGtMask", "SubgroupLeMask", "SubgroupLtMask"), Values("GLCompute"), Values("Input"), Values("%f32vec4"), + Values("VUID-SubgroupEqMask-SubgroupEqMask-04371 " + "VUID-SubgroupGeMask-SubgroupGeMask-04373 " + "VUID-SubgroupGtMask-SubgroupGtMask-04375 " + "VUID-SubgroupLeMask-SubgroupLeMask-04377 " + "VUID-SubgroupLtMask-SubgroupLtMask-04379"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 4-component 32-bit int vector")))); @@ -4131,6 +3613,11 @@ INSTANTIATE_TEST_SUITE_P( "SubgroupLeMask", "SubgroupLtMask"), Values("GLCompute"), Values("Output", "Workgroup", "Private"), Values("%u32vec4"), + Values("VUID-SubgroupEqMask-SubgroupEqMask-04370 " + "VUID-SubgroupGeMask-SubgroupGeMask-04372 " + "VUID-SubgroupGtMask-SubgroupGtMask-04374 " + "VUID-SubgroupLeMask-SubgroupLeMask-04376 " + "VUID-SubgroupLtMask-SubgroupLtMask-04378"), Values(TestResult( SPV_ERROR_INVALID_DATA, "to be only used for variables with Input storage class")))); @@ -4140,7 +3627,7 @@ INSTANTIATE_TEST_SUITE_P(SubgroupMaskOk, ValidateVulkanSubgroupBuiltIns, "SubgroupGtMask", "SubgroupLeMask", "SubgroupLtMask"), Values("GLCompute"), Values("Input"), - Values("%u32vec4"), + Values("%u32vec4"), Values(nullptr), Values(TestResult(SPV_SUCCESS, "")))); TEST_F(ValidateBuiltIns, SubgroupMaskMemberDecorate) { @@ -4173,6 +3660,8 @@ INSTANTIATE_TEST_SUITE_P( SubgroupInvocationIdAndSizeNotU32, ValidateVulkanSubgroupBuiltIns, Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"), Values("GLCompute"), Values("Input"), Values("%f32"), + Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-" + "04381 VUID-SubgroupSize-SubgroupSize-04383"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 32-bit int")))); @@ -4181,6 +3670,8 @@ INSTANTIATE_TEST_SUITE_P( Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"), Values("GLCompute"), Values("Output", "Workgroup", "Private"), Values("%u32"), + Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-" + "04380 VUID-SubgroupSize-SubgroupSize-04382"), Values(TestResult( SPV_ERROR_INVALID_DATA, "to be only used for variables with Input storage class")))); @@ -4189,7 +3680,7 @@ INSTANTIATE_TEST_SUITE_P( SubgroupInvocationIdAndSizeOk, ValidateVulkanSubgroupBuiltIns, Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"), Values("GLCompute"), Values("Input"), Values("%u32"), - Values(TestResult(SPV_SUCCESS, "")))); + Values(nullptr), Values(TestResult(SPV_SUCCESS, "")))); TEST_F(ValidateBuiltIns, SubgroupSizeMemberDecorate) { const std::string text = R"( @@ -4217,9 +3708,21 @@ OpFunctionEnd } INSTANTIATE_TEST_SUITE_P( + SubgroupNumAndIdNotCompute, ValidateVulkanSubgroupBuiltIns, + Combine( + Values("SubgroupId", "NumSubgroups"), Values("Vertex"), Values("Input"), + Values("%u32"), + Values("VUID-SubgroupId-SubgroupId-04367 " + "VUID-NumSubgroups-NumSubgroups-04293"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute execution model")))); + +INSTANTIATE_TEST_SUITE_P( SubgroupNumAndIdNotU32, ValidateVulkanSubgroupBuiltIns, Combine(Values("SubgroupId", "NumSubgroups"), Values("GLCompute"), Values("Input"), Values("%f32"), + Values("VUID-SubgroupId-SubgroupId-04369 " + "VUID-NumSubgroups-NumSubgroups-04295"), Values(TestResult(SPV_ERROR_INVALID_DATA, "needs to be a 32-bit int")))); @@ -4227,6 +3730,8 @@ INSTANTIATE_TEST_SUITE_P( SubgroupNumAndIdNotInput, ValidateVulkanSubgroupBuiltIns, Combine(Values("SubgroupId", "NumSubgroups"), Values("GLCompute"), Values("Output", "Workgroup", "Private"), Values("%u32"), + Values("VUID-SubgroupId-SubgroupId-04368 " + "VUID-NumSubgroups-NumSubgroups-04294"), Values(TestResult( SPV_ERROR_INVALID_DATA, "to be only used for variables with Input storage class")))); @@ -4234,7 +3739,7 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(SubgroupNumAndIdOk, ValidateVulkanSubgroupBuiltIns, Combine(Values("SubgroupId", "NumSubgroups"), Values("GLCompute"), Values("Input"), - Values("%u32"), + Values("%u32"), Values(nullptr), Values(TestResult(SPV_SUCCESS, "")))); TEST_F(ValidateBuiltIns, SubgroupIdMemberDecorate) { @@ -4436,6 +3941,184 @@ INSTANTIATE_TEST_SUITE_P( Values(TestResult(SPV_ERROR_INVALID_DATA, "According to the Vulkan spec BuiltIn ShadingRateKHR " "variable needs to be a 32-bit int scalar.")))); + +INSTANTIATE_TEST_SUITE_P( + FragInvocationCountInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragInvocationCountEXT"), Values("Fragment"), + Values("Input"), Values("%u32"), + Values("OpCapability FragmentDensityEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FragInvocationCountInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("FragInvocationCountEXT"), Values("Vertex"), Values("Input"), + Values("%u32"), Values("OpCapability FragmentDensityEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"), + Values("VUID-FragInvocationCountEXT-FragInvocationCountEXT-04217"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn FragInvocationCountEXT " + "to be used only with Fragment execution model.")))); + +INSTANTIATE_TEST_SUITE_P( + FragInvocationCountInvalidStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragInvocationCountEXT"), Values("Fragment"), + Values("Output"), Values("%u32"), + Values("OpCapability FragmentDensityEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"), + Values("VUID-FragInvocationCountEXT-FragInvocationCountEXT-04218"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn FragInvocationCountEXT to be only " + "used for variables with Input storage class.")))); + +INSTANTIATE_TEST_SUITE_P( + FragInvocationCountInvalidType, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragInvocationCountEXT"), Values("Fragment"), + Values("Input"), Values("%f32"), + Values("OpCapability FragmentDensityEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"), + Values("VUID-FragInvocationCountEXT-FragInvocationCountEXT-04219"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "According to the Vulkan spec BuiltIn FragInvocationCountEXT " + "variable needs to be a 32-bit int scalar.")))); + +INSTANTIATE_TEST_SUITE_P( + FragSizeInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragSizeEXT"), Values("Fragment"), Values("Input"), + Values("%u32vec2"), Values("OpCapability FragmentDensityEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FragSizeInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragSizeEXT"), Values("Vertex"), Values("Input"), + Values("%u32vec2"), Values("OpCapability FragmentDensityEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"), + Values("VUID-FragSizeEXT-FragSizeEXT-04220"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn FragSizeEXT to be " + "used only with Fragment execution model.")))); + +INSTANTIATE_TEST_SUITE_P( + FragSizeInvalidStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("FragSizeEXT"), Values("Fragment"), Values("Output"), + Values("%u32vec2"), Values("OpCapability FragmentDensityEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"), + Values("VUID-FragSizeEXT-FragSizeEXT-04221"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn FragSizeEXT to be only " + "used for variables with Input storage class.")))); + +INSTANTIATE_TEST_SUITE_P( + FragSizeInvalidType, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragSizeEXT"), Values("Fragment"), Values("Input"), + Values("%u32vec3"), Values("OpCapability FragmentDensityEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_invocation_density\"\n"), + Values("VUID-FragSizeEXT-FragSizeEXT-04222"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "According to the Vulkan spec BuiltIn FragSizeEXT variable " + "needs to be a 2-component 32-bit int vector.")))); + +INSTANTIATE_TEST_SUITE_P( + FragStencilRefOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragStencilRefEXT"), Values("Fragment"), Values("Output"), + Values("%u32", "%u64"), Values("OpCapability StencilExportEXT\n"), + Values("OpExtension \"SPV_EXT_shader_stencil_export\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FragStencilRefInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragStencilRefEXT"), Values("Vertex"), Values("Output"), + Values("%u32", "%u64"), Values("OpCapability StencilExportEXT\n"), + Values("OpExtension \"SPV_EXT_shader_stencil_export\"\n"), + Values("VUID-FragStencilRefEXT-FragStencilRefEXT-04223"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn FragStencilRefEXT to " + "be used only with Fragment execution model.")))); + +INSTANTIATE_TEST_SUITE_P( + FragStencilRefInvalidStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragStencilRefEXT"), Values("Fragment"), Values("Input"), + Values("%u32", "%u64"), Values("OpCapability StencilExportEXT\n"), + Values("OpExtension \"SPV_EXT_shader_stencil_export\"\n"), + Values("VUID-FragStencilRefEXT-FragStencilRefEXT-04224"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn FragStencilRefEXT to be only used " + "for variables with Output storage class.")))); + +INSTANTIATE_TEST_SUITE_P( + FragStencilRefInvalidType, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FragStencilRefEXT"), Values("Fragment"), Values("Output"), + Values("%f32", "%f64", "%u32vec2"), + Values("OpCapability StencilExportEXT\n"), + Values("OpExtension \"SPV_EXT_shader_stencil_export\"\n"), + Values("VUID-FragStencilRefEXT-FragStencilRefEXT-04225"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "According to the Vulkan spec BuiltIn FragStencilRefEXT " + "variable needs to be a int scalar.")))); + +INSTANTIATE_TEST_SUITE_P( + FullyCoveredEXTInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FullyCoveredEXT"), Values("Fragment"), Values("Input"), + Values("%bool"), Values("OpCapability FragmentFullyCoveredEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_fully_covered\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FullyCoveredEXTInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FullyCoveredEXT"), Values("Vertex"), Values("Input"), + Values("%bool"), Values("OpCapability FragmentFullyCoveredEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_fully_covered\"\n"), + Values("VUID-FullyCoveredEXT-FullyCoveredEXT-04232"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn FullyCoveredEXT to " + "be used only with Fragment execution model.")))); + +INSTANTIATE_TEST_SUITE_P( + FullyCoveredEXTInvalidStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FullyCoveredEXT"), Values("Fragment"), Values("Output"), + Values("%bool"), Values("OpCapability FragmentFullyCoveredEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_fully_covered\"\n"), + Values("VUID-FullyCoveredEXT-FullyCoveredEXT-04233"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn FullyCoveredEXT to be only used " + "for variables with Input storage class.")))); + +INSTANTIATE_TEST_SUITE_P( + FullyCoveredEXTInvalidType, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("FullyCoveredEXT"), Values("Fragment"), Values("Input"), + Values("%f32"), Values("OpCapability FragmentFullyCoveredEXT\n"), + Values("OpExtension \"SPV_EXT_fragment_fully_covered\"\n"), + Values("VUID-FullyCoveredEXT-FullyCoveredEXT-04234"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "According to the Vulkan spec BuiltIn FullyCoveredEXT variable " + "needs to be a bool scalar.")))); + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index 9705cb8f..82f8d381 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -116,8 +116,6 @@ using ValidateCapabilityOpenGL40 = spvtest::ValidateBase<CapTestParameter>; using ValidateCapabilityVulkan11 = spvtest::ValidateBase<CapTestParameter>; // Always assembles using Vulkan 1.2. using ValidateCapabilityVulkan12 = spvtest::ValidateBase<CapTestParameter>; -// Always assembles using WebGPU. -using ValidateCapabilityWebGPU = spvtest::ValidateBase<CapTestParameter>; TEST_F(ValidateCapability, Default) { const char str[] = R"( @@ -588,18 +586,6 @@ const std::vector<std::string>& AllVulkan12Capabilities() { return *r; } -const std::vector<std::string>& AllWebGPUCapabilities() { - static const auto r = new std::vector<std::string>{ - "", - "Shader", - "Matrix", - "Sampled1D", - "Image1D", - "ImageQuery", - "DerivativeControl"}; - return *r; -} - const std::vector<std::string>& MatrixDependencies() { static const auto r = new std::vector<std::string>{ "Matrix", @@ -790,12 +776,6 @@ const char kGLSL450MemoryModel[] = \ " OpCapability Shader" " OpMemoryModel Logical GLSL450 "; -const char kVulkanMemoryModel[] = \ - " OpCapability Shader" - " OpCapability VulkanMemoryModelKHR" - " OpExtension \"SPV_KHR_vulkan_memory_model\"" - " OpMemoryModel Logical VulkanKHR "; - const char kVoidFVoid[] = \ " %void = OpTypeVoid" " %void_f = OpTypeFunction %void" @@ -1814,16 +1794,6 @@ std::make_pair(std::string(kGLSL450MemoryModel) + AllSpirV10Capabilities()) ))); -INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityWebGPU, - Combine( - // All capabilities to try. - ValuesIn(AllCapabilities()), - Values( -std::make_pair(std::string(kVulkanMemoryModel) + - "OpEntryPoint Vertex %func \"shader\" \n" + std::string(kVoidFVoid), - AllWebGPUCapabilities()) -))); - INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan11, Combine( // All capabilities to try. @@ -2047,17 +2017,6 @@ TEST_P(ValidateCapabilityOpenGL40, Capability) { } } -TEST_P(ValidateCapabilityWebGPU, Capability) { - const std::string capability = Capability(GetParam()); - if (Exists(capability, SPV_ENV_WEBGPU_0)) { - const std::string test_code = MakeAssembly(GetParam()); - CompileSuccessfully(test_code, SPV_ENV_WEBGPU_0); - ASSERT_EQ(ExpectedResult(GetParam()), - ValidateInstructions(SPV_ENV_WEBGPU_0)) - << test_code; - } -} - TEST_F(ValidateCapability, SemanticsIdIsAnIdNotALiteral) { // From https://github.com/KhronosGroup/SPIRV-Tools/issues/248 // The validator was interpreting the memory semantics ID number diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 7a268ebb..9698fb1c 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -168,15 +168,6 @@ const std::string& GetDefaultHeader(SpvCapability cap) { return (cap == SpvCapabilityShader) ? shader_header : kernel_header; } -const std::string& GetWebGPUHeader() { - static const std::string header = - "OpCapability Shader\n" - "OpCapability VulkanMemoryModelKHR\n" - "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" - "OpMemoryModel Logical VulkanKHR\n"; - return header; -} - const std::string& types_consts() { static const std::string types = "%voidt = OpTypeVoid\n" @@ -714,10 +705,8 @@ TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) { } } -std::string GetUnreachableMergeNoMergeInst(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableMergeNoMergeInst(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranchConditional); Block t("t", SpvOpReturn); @@ -725,17 +714,11 @@ std::string GetUnreachableMergeNoMergeInst(SpvCapability cap, Block merge("merge", SpvOpReturn); entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); - if (!spvIsWebGPUEnv(env) && cap == SpvCapabilityShader) + if (cap == SpvCapabilityShader) branch.AppendBody("OpSelectionMerge %merge None\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", std::make_pair("func", "Main")); str += types_consts() + "%func = OpFunction %voidt None %funct\n"; str += entry >> branch; str += branch >> std::vector<Block>({t, f}); @@ -748,23 +731,12 @@ std::string GetUnreachableMergeNoMergeInst(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableMergeNoMergeInst) { - CompileSuccessfully( - GetUnreachableMergeNoMergeInst(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableMergeNoMergeInst(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableMergeNoMergeInst) { - CompileSuccessfully( - GetUnreachableMergeNoMergeInst(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, all blocks must be reachable")); -} - -std::string GetUnreachableMergeTerminatedBy(SpvCapability cap, - spv_target_env env, SpvOp op) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableMergeTerminatedBy(SpvCapability cap, SpvOp op) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranchConditional); @@ -774,16 +746,10 @@ std::string GetUnreachableMergeTerminatedBy(SpvCapability cap, entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) branch.AppendBody("OpSelectionMerge %merge None\n"); - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += entry >> branch; @@ -797,49 +763,24 @@ std::string GetUnreachableMergeTerminatedBy(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpUnreachable) { - CompileSuccessfully(GetUnreachableMergeTerminatedBy( - GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpUnreachable)); + CompileSuccessfully( + GetUnreachableMergeTerminatedBy(GetParam(), SpvOpUnreachable)); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateCFG, UnreachableMergeTerminatedByOpKill) { - CompileSuccessfully(GetUnreachableMergeTerminatedBy( - SpvCapabilityShader, SPV_ENV_UNIVERSAL_1_0, SpvOpKill)); + CompileSuccessfully( + GetUnreachableMergeTerminatedBy(SpvCapabilityShader, SpvOpKill)); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpReturn) { - CompileSuccessfully(GetUnreachableMergeTerminatedBy( - GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpReturn)); + CompileSuccessfully(GetUnreachableMergeTerminatedBy(GetParam(), SpvOpReturn)); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpUnreachable) { - CompileSuccessfully(GetUnreachableMergeTerminatedBy( - SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpUnreachable)); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpKill) { - CompileSuccessfully(GetUnreachableMergeTerminatedBy( - SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpKill)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("must terminate with OpUnreachable")); -} - -TEST_P(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpReturn) { - CompileSuccessfully(GetUnreachableMergeTerminatedBy( - SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpReturn)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("must terminate with OpUnreachable")); -} - -std::string GetUnreachableContinueTerminatedBy(SpvCapability cap, - spv_target_env env, SpvOp op) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableContinueTerminatedBy(SpvCapability cap, SpvOp op) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranch); @@ -849,16 +790,10 @@ std::string GetUnreachableContinueTerminatedBy(SpvCapability cap, if (op == SpvOpBranch) target >> branch; std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) branch.AppendBody("OpLoopMerge %merge %target None\n"); - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += entry >> branch; @@ -871,8 +806,8 @@ std::string GetUnreachableContinueTerminatedBy(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpUnreachable) { - CompileSuccessfully(GetUnreachableContinueTerminatedBy( - GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpUnreachable)); + CompileSuccessfully( + GetUnreachableContinueTerminatedBy(GetParam(), SpvOpUnreachable)); if (GetParam() == SpvCapabilityShader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -883,16 +818,16 @@ TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpUnreachable) { } TEST_F(ValidateCFG, UnreachableContinueTerminatedBySpvOpKill) { - CompileSuccessfully(GetUnreachableContinueTerminatedBy( - SpvCapabilityShader, SPV_ENV_UNIVERSAL_1_0, SpvOpKill)); + CompileSuccessfully( + GetUnreachableContinueTerminatedBy(SpvCapabilityShader, SpvOpKill)); ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("targeted by 0 back-edge blocks")); } TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpReturn) { - CompileSuccessfully(GetUnreachableContinueTerminatedBy( - GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpReturn)); + CompileSuccessfully( + GetUnreachableContinueTerminatedBy(GetParam(), SpvOpReturn)); if (GetParam() == SpvCapabilityShader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -903,48 +838,13 @@ TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpReturn) { } TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpBranch) { - CompileSuccessfully(GetUnreachableContinueTerminatedBy( - GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpBranch)); + CompileSuccessfully( + GetUnreachableContinueTerminatedBy(GetParam(), SpvOpBranch)); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpUnreachable) { - CompileSuccessfully(GetUnreachableContinueTerminatedBy( - SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpUnreachable)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, unreachable continue-target must " - "terminate with OpBranch.\n %12 = OpLabel\n")); -} - -TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpKill) { - CompileSuccessfully(GetUnreachableContinueTerminatedBy( - SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpKill)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, unreachable continue-target must " - "terminate with OpBranch.\n %12 = OpLabel\n")); -} - -TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpReturn) { - CompileSuccessfully(GetUnreachableContinueTerminatedBy( - SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpReturn)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, unreachable continue-target must " - "terminate with OpBranch.\n %12 = OpLabel\n")); -} - -TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpBranch) { - CompileSuccessfully(GetUnreachableContinueTerminatedBy( - SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpBranch)); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block body("body", SpvOpReturn); Block entry("entry"); @@ -955,16 +855,10 @@ std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap, entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) branch.AppendBody("OpSelectionMerge %merge None\n"); - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += body; @@ -979,23 +873,12 @@ std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableMergeUnreachableMergeInst) { - CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst( - GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableMergeUnreachableMergeInst) { - CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst( - SpvCapabilityShader, SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("must be referenced by a reachable merge instruction")); -} - -std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block body("body", SpvOpReturn); Block entry("entry"); @@ -1006,16 +889,10 @@ std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap, target >> branch; std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) branch.AppendBody("OpLoopMerge %merge %target None\n"); - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += body; @@ -1029,8 +906,7 @@ std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) { - CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst( - GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(GetParam())); if (GetParam() == SpvCapabilityShader) { // Shader causes additional structured CFG checks that cause a failure. ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); @@ -1043,18 +919,8 @@ TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) { } } -TEST_F(ValidateCFG, WebGPUUnreachableContinueUnreachableLoopInst) { - CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst( - SpvCapabilityShader, SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("must be referenced by a reachable loop instruction")); -} - -std::string GetUnreachableMergeWithComplexBody(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableMergeWithComplexBody(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranchConditional); @@ -1062,23 +928,15 @@ std::string GetUnreachableMergeWithComplexBody(SpvCapability cap, Block f("f", SpvOpReturn); Block merge("merge", SpvOpUnreachable); - entry.AppendBody(spvIsWebGPUEnv(env) - ? "%placeholder = OpVariable %intptrt Function %two\n" - : "%placeholder = OpVariable %intptrt Function\n"); + entry.AppendBody("%placeholder = OpVariable %intptrt Function\n"); entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n"); merge.AppendBody("OpStore %placeholder %one\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) branch.AppendBody("OpSelectionMerge %merge None\n"); - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", std::make_pair("func", "Main")); str += types_consts(); str += "%intptrt = OpTypePointer Function %intt\n"; str += "%func = OpFunction %voidt None %funct\n"; @@ -1093,24 +951,12 @@ std::string GetUnreachableMergeWithComplexBody(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableMergeWithComplexBody) { - CompileSuccessfully( - GetUnreachableMergeWithComplexBody(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableMergeWithComplexBody(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableMergeWithComplexBody) { - CompileSuccessfully(GetUnreachableMergeWithComplexBody(SpvCapabilityShader, - SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("must only contain an OpLabel and OpUnreachable instruction")); -} - -std::string GetUnreachableContinueWithComplexBody(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableContinueWithComplexBody(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranch); @@ -1119,22 +965,14 @@ std::string GetUnreachableContinueWithComplexBody(SpvCapability cap, target >> branch; - entry.AppendBody(spvIsWebGPUEnv(env) - ? "%placeholder = OpVariable %intptrt Function %two\n" - : "%placeholder = OpVariable %intptrt Function\n"); + entry.AppendBody("%placeholder = OpVariable %intptrt Function\n"); target.AppendBody("OpStore %placeholder %one\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) branch.AppendBody("OpLoopMerge %merge %target None\n"); - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); str += types_consts(); str += "%intptrt = OpTypePointer Function %intt\n"; str += "%func = OpFunction %voidt None %funct\n"; @@ -1148,24 +986,12 @@ std::string GetUnreachableContinueWithComplexBody(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableContinueWithComplexBody) { - CompileSuccessfully( - GetUnreachableContinueWithComplexBody(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableContinueWithComplexBody(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableContinueWithComplexBody) { - CompileSuccessfully(GetUnreachableContinueWithComplexBody(SpvCapabilityShader, - SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("must only contain an OpLabel and an OpBranch instruction")); -} - -std::string GetUnreachableMergeWithBranchUse(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableMergeWithBranchUse(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranchConditional); @@ -1176,16 +1002,10 @@ std::string GetUnreachableMergeWithBranchUse(SpvCapability cap, entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) branch.AppendBody("OpSelectionMerge %merge None\n"); - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += entry >> branch; @@ -1199,15 +1019,12 @@ std::string GetUnreachableMergeWithBranchUse(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) { - CompileSuccessfully( - GetUnreachableMergeWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableMergeWithBranchUse(GetParam())); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranchConditional); @@ -1219,18 +1036,12 @@ std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap, entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) { branch.AppendBody("OpSelectionMerge %merge None\n"); duplicate.AppendBody("OpSelectionMerge %merge None\n"); } - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += entry >> branch; @@ -1245,8 +1056,7 @@ std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) { - CompileSuccessfully( - GetUnreachableMergeWithMultipleUses(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableMergeWithMultipleUses(GetParam())); if (GetParam() == SpvCapabilityShader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), @@ -1256,18 +1066,8 @@ TEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) { } } -TEST_F(ValidateCFG, WebGPUUnreachableMergeWithMultipleUses) { - CompileSuccessfully(GetUnreachableMergeWithMultipleUses(SpvCapabilityShader, - SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("is already a merge block for another header")); -} - -std::string GetUnreachableContinueWithBranchUse(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableContinueWithBranchUse(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block foo("foo", SpvOpBranch); @@ -1278,21 +1078,13 @@ std::string GetUnreachableContinueWithBranchUse(SpvCapability cap, foo >> target; target >> branch; - entry.AppendBody(spvIsWebGPUEnv(env) - ? "%placeholder = OpVariable %intptrt Function %two\n" - : "%placeholder = OpVariable %intptrt Function\n"); + entry.AppendBody("%placeholder = OpVariable %intptrt Function\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) branch.AppendBody("OpLoopMerge %merge %target None\n"); - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); + str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); str += types_consts(); str += "%intptrt = OpTypePointer Function %intt\n"; str += "%func = OpFunction %voidt None %funct\n"; @@ -1307,23 +1099,12 @@ std::string GetUnreachableContinueWithBranchUse(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableContinueWithBranchUse) { - CompileSuccessfully( - GetUnreachableContinueWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableContinueWithBranchUse(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableContinueWithBranchUse) { - CompileSuccessfully(GetUnreachableContinueWithBranchUse(SpvCapabilityShader, - SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("cannot be the target of a branch.")); -} - -std::string GetReachableMergeAndContinue(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetReachableMergeAndContinue(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranch); @@ -1339,20 +1120,13 @@ std::string GetReachableMergeAndContinue(SpvCapability cap, f >> target; std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) { branch.AppendBody("OpLoopMerge %merge %target None\n"); body.AppendBody("OpSelectionMerge %f None\n"); } - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", "target", "body", "t", "f", - std::make_pair("func", "Main")); - + str += nameOps("branch", "merge", "target", "body", "t", "f", + std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += entry >> branch; @@ -1368,21 +1142,12 @@ std::string GetReachableMergeAndContinue(SpvCapability cap, } TEST_P(ValidateCFG, ReachableMergeAndContinue) { - CompileSuccessfully( - GetReachableMergeAndContinue(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetReachableMergeAndContinue(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUReachableMergeAndContinue) { - CompileSuccessfully( - GetReachableMergeAndContinue(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -std::string GetUnreachableMergeAndContinue(SpvCapability cap, - spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableMergeAndContinue(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block branch("branch", SpvOpBranch); @@ -1396,20 +1161,13 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap, body.SetBody("%cond = OpSLessThan %boolt %one %two\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } if (cap == SpvCapabilityShader) { branch.AppendBody("OpLoopMerge %merge %target None\n"); body.AppendBody("OpSelectionMerge %target None\n"); } - if (!spvIsWebGPUEnv(env)) - str += nameOps("branch", "merge", "target", "body", "t", "f", - std::make_pair("func", "Main")); - + str += nameOps("branch", "merge", "target", "body", "t", "f", + std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += entry >> branch; @@ -1425,36 +1183,19 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap, } TEST_P(ValidateCFG, UnreachableMergeAndContinue) { - CompileSuccessfully( - GetUnreachableMergeAndContinue(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableMergeAndContinue(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableMergeAndContinue) { - CompileSuccessfully( - GetUnreachableMergeAndContinue(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("unreachable merge-blocks must terminate with OpUnreachable")); -} - -std::string GetUnreachableBlock(SpvCapability cap, spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableBlock(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block unreachable("unreachable"); Block exit("exit", SpvOpReturn); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } - if (!spvIsWebGPUEnv(env)) - str += nameOps("unreachable", "exit", std::make_pair("func", "Main")); + str += nameOps("unreachable", "exit", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; str += entry >> exit; @@ -1466,20 +1207,12 @@ std::string GetUnreachableBlock(SpvCapability cap, spv_target_env env) { } TEST_P(ValidateCFG, UnreachableBlock) { - CompileSuccessfully(GetUnreachableBlock(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableBlock(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableBlock) { - CompileSuccessfully( - GetUnreachableBlock(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), HasSubstr("all blocks must be reachable")); -} - -std::string GetUnreachableBranch(SpvCapability cap, spv_target_env env) { - std::string header = - spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); +std::string GetUnreachableBranch(SpvCapability cap) { + std::string header = GetDefaultHeader(cap); Block entry("entry"); Block unreachable("unreachable", SpvOpBranchConditional); @@ -1493,13 +1226,7 @@ std::string GetUnreachableBranch(SpvCapability cap, spv_target_env env) { unreachable.AppendBody("OpSelectionMerge %merge None\n"); std::string str = header; - if (spvIsWebGPUEnv(env)) { - str += - "OpEntryPoint Fragment %func \"func\"\n" - "OpExecutionMode %func OriginUpperLeft\n"; - } - if (!spvIsWebGPUEnv(env)) - str += nameOps("unreachable", "exit", std::make_pair("func", "Main")); + str += nameOps("unreachable", "exit", std::make_pair("func", "Main")); str += types_consts(); str += "%func = OpFunction %voidt None %funct\n"; @@ -1516,17 +1243,10 @@ std::string GetUnreachableBranch(SpvCapability cap, spv_target_env env) { } TEST_P(ValidateCFG, UnreachableBranch) { - CompileSuccessfully(GetUnreachableBranch(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + CompileSuccessfully(GetUnreachableBranch(GetParam())); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, WebGPUUnreachableBranch) { - CompileSuccessfully( - GetUnreachableBranch(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); - ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), HasSubstr("all blocks must be reachable")); -} - TEST_P(ValidateCFG, EmptyFunction) { std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) + R"(%func = OpFunction %voidt None %funct diff --git a/test/val/val_code_generator.cpp b/test/val/val_code_generator.cpp index 96971f9b..f151f51a 100644 --- a/test/val/val_code_generator.cpp +++ b/test/val/val_code_generator.cpp @@ -32,13 +32,6 @@ OpCapability SampleRateShading )"; } -std::string GetWebGPUShaderCapabilities() { - return R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -)"; -} - std::string GetDefaultShaderTypes() { return R"( %void = OpTypeVoid @@ -127,55 +120,6 @@ std::string GetDefaultShaderTypes() { )"; } -std::string GetWebGPUShaderTypes() { - return R"( -%void = OpTypeVoid -%func = OpTypeFunction %void -%bool = OpTypeBool -%f32 = OpTypeFloat 32 -%u32 = OpTypeInt 32 0 -%f32vec2 = OpTypeVector %f32 2 -%f32vec3 = OpTypeVector %f32 3 -%f32vec4 = OpTypeVector %f32 4 -%u32vec2 = OpTypeVector %u32 2 -%u32vec3 = OpTypeVector %u32 3 -%u32vec4 = OpTypeVector %u32 4 - -%f32_0 = OpConstant %f32 0 -%f32_1 = OpConstant %f32 1 -%f32_2 = OpConstant %f32 2 -%f32_3 = OpConstant %f32 3 -%f32_4 = OpConstant %f32 4 -%f32_h = OpConstant %f32 0.5 -%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 -%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 -%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 -%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 -%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 -%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 - -%u32_0 = OpConstant %u32 0 -%u32_1 = OpConstant %u32 1 -%u32_2 = OpConstant %u32 2 -%u32_3 = OpConstant %u32 3 -%u32_4 = OpConstant %u32 4 - -%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 -%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 -%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 - -%u32arr2 = OpTypeArray %u32 %u32_2 -%u32arr3 = OpTypeArray %u32 %u32_3 -%u32arr4 = OpTypeArray %u32 %u32_4 -%f32arr2 = OpTypeArray %f32 %u32_2 -%f32arr3 = OpTypeArray %f32 %u32_3 -%f32arr4 = OpTypeArray %f32 %u32_4 - -%f32vec3arr3 = OpTypeArray %f32vec3 %u32_3 -%f32vec4arr3 = OpTypeArray %f32vec4 %u32_3 -)"; -} - } // namespace CodeGenerator CodeGenerator::GetDefaultShaderCodeGenerator() { @@ -186,15 +130,6 @@ CodeGenerator CodeGenerator::GetDefaultShaderCodeGenerator() { return generator; } -CodeGenerator CodeGenerator::GetWebGPUShaderCodeGenerator() { - CodeGenerator generator; - generator.capabilities_ = GetWebGPUShaderCapabilities(); - generator.memory_model_ = "OpMemoryModel Logical VulkanKHR\n"; - generator.extensions_ = "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"; - generator.types_ = GetWebGPUShaderTypes(); - return generator; -} - std::string CodeGenerator::Build() const { std::ostringstream ss; diff --git a/test/val/val_code_generator.h b/test/val/val_code_generator.h index e580ddff..c69deeec 100644 --- a/test/val/val_code_generator.h +++ b/test/val/val_code_generator.h @@ -31,7 +31,6 @@ struct EntryPoint { class CodeGenerator { public: static CodeGenerator GetDefaultShaderCodeGenerator(); - static CodeGenerator GetWebGPUShaderCodeGenerator(); std::string Build() const; diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp index 47e67938..b9802ece 100644 --- a/test/val/val_conversion_test.cpp +++ b/test/val/val_conversion_test.cpp @@ -1464,16 +1464,16 @@ OpFunctionEnd TEST_F(ValidateConversion, ConvertUToPtrPSBSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %void = OpTypeVoid %voidfn = OpTypeFunction %void %main = OpFunction %void None %voidfn @@ -1489,11 +1489,11 @@ OpFunctionEnd TEST_F(ValidateConversion, ConvertUToPtrPSBStorageClass) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %uint64 = OpTypeInt 64 0 @@ -1513,22 +1513,54 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Pointer storage class must be " - "PhysicalStorageBufferEXT: ConvertUToPtr")); + "PhysicalStorageBuffer: ConvertUToPtr")); +} + +TEST_F(ValidateConversion, ConvertUToPtrVulkanWrongWidth) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%uint32 = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u32_1 = OpConstant %uint32 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpConvertUToPtr %ptr %u32_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("PhysicalStorageBuffer64 addressing mode requires the input " + "integer to have a 64-bit width for Vulkan environment.")); } TEST_F(ValidateConversion, ConvertPtrToUPSBSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %val1 RestrictPointerEXT %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1547,11 +1579,11 @@ OpFunctionEnd TEST_F(ValidateConversion, ConvertPtrToUPSBStorageClass) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %uint64 = OpTypeInt 64 0 @@ -1571,7 +1603,42 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Pointer storage class must be " - "PhysicalStorageBufferEXT: ConvertPtrToU")); + "PhysicalStorageBuffer: ConvertPtrToU")); +} + +TEST_F(ValidateConversion, ConvertPtrToUVulkanWrongWidth) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 RestrictPointerEXT +%uint32 = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpConvertPtrToU %uint32 %val2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("PhysicalStorageBuffer64 addressing mode requires the result " + "integer type to have a 64-bit width for Vulkan environment.")); } using ValidateSmallConversions = spvtest::ValidateBase<std::string>; diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp index 30afd03c..1d4c0e04 100644 --- a/test/val/val_data_test.cpp +++ b/test/val/val_data_test.cpp @@ -36,24 +36,6 @@ std::string HeaderWith(std::string cap) { cap + " OpMemoryModel Logical GLSL450 "; } -std::string WebGPUHeaderWith(std::string cap) { - return R"( -OpCapability Shader -OpCapability )" + - cap + R"( -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -)"; -} - -std::string webgpu_header = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -)"; - std::string header = R"( OpCapability Shader OpCapability Linkage @@ -267,18 +249,6 @@ TEST_F(ValidateData, int8_with_storage_push_constant_8_good) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); } -TEST_F(ValidateData, webgpu_int8_bad) { - std::string str = WebGPUHeaderWith("Int8") + "%2 = OpTypeInt 8 0"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Capability Int8 is not allowed by WebGPU specification (or " - "requires extension)\n" - " OpCapability Int8\n")); -} - TEST_F(ValidateData, int16_good) { std::string str = header_with_int16 + "%2 = OpTypeInt 16 1"; CompileSuccessfully(str.c_str()); @@ -338,34 +308,6 @@ TEST_F(ValidateData, int16_bad) { EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int16_cap_error)); } -TEST_F(ValidateData, webgpu_int16_bad) { - std::string str = WebGPUHeaderWith("Int16") + "%2 = OpTypeInt 16 1"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Capability Int16 is not allowed by WebGPU specification (or " - "requires extension)\n" - " OpCapability Int16\n")); -} - -TEST_F(ValidateData, webgpu_int32_good) { - std::string str = webgpu_header + R"( - OpEntryPoint Fragment %func "func" - OpExecutionMode %func OriginUpperLeft -%uint_t = OpTypeInt 32 0 - %void = OpTypeVoid -%func_t = OpTypeFunction %void - %func = OpFunction %void None %func_t - %1 = OpLabel - OpReturn - OpFunctionEnd -)"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - TEST_F(ValidateData, int64_good) { std::string str = header_with_int64 + "%2 = OpTypeInt 64 1"; CompileSuccessfully(str.c_str()); @@ -379,18 +321,6 @@ TEST_F(ValidateData, int64_bad) { EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int64_cap_error)); } -TEST_F(ValidateData, webgpu_int64_bad) { - std::string str = WebGPUHeaderWith("Int64") + "%2 = OpTypeInt 64 1"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Capability Int64 is not allowed by WebGPU specification (or " - "requires extension)\n" - " OpCapability Int64\n")); -} - // Number of bits in an integer may be only one of: {8,16,32,64} TEST_F(ValidateData, int_invalid_num_bits) { std::string str = header + "%2 = OpTypeInt 48 1"; @@ -418,34 +348,6 @@ TEST_F(ValidateData, float16_bad) { EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float16_cap_error)); } -TEST_F(ValidateData, webgpu_float16_bad) { - std::string str = WebGPUHeaderWith("Float16") + "%2 = OpTypeFloat 16"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Capability Float16 is not allowed by WebGPU specification (or " - "requires extension)\n" - " OpCapability Float16\n")); -} - -TEST_F(ValidateData, webgpu_float32_good) { - std::string str = webgpu_header + R"( - OpEntryPoint Fragment %func "func" - OpExecutionMode %func OriginUpperLeft -%float_t = OpTypeFloat 32 - %void = OpTypeVoid - %func_t = OpTypeFunction %void - %func = OpFunction %void None %func_t - %1 = OpLabel - OpReturn - OpFunctionEnd -)"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - TEST_F(ValidateData, float64_good) { std::string str = header_with_float64 + "%2 = OpTypeFloat 64"; CompileSuccessfully(str.c_str()); @@ -459,18 +361,6 @@ TEST_F(ValidateData, float64_bad) { EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float64_cap_error)); } -TEST_F(ValidateData, webgpu_float64_bad) { - std::string str = WebGPUHeaderWith("Float64") + "%2 = OpTypeFloat 64"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Capability Float64 is not allowed by WebGPU specification (or " - "requires extension)\n" - " OpCapability Float64\n")); -} - // Number of bits in a float may be only one of: {16,32,64} TEST_F(ValidateData, float_invalid_num_bits) { std::string str = header + "%2 = OpTypeFloat 48"; @@ -497,7 +387,7 @@ TEST_F(ValidateData, ids_should_be_validated_before_data) { CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("ID 3[%3] has not been defined")); + HasSubstr("Operand 3[%3] requires a previous definition")); } TEST_F(ValidateData, matrix_bad_column_type) { @@ -882,66 +772,58 @@ TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) { "OpTypeStruct %_runtimearr_uint %uint\n")); } -TEST_F(ValidateData, webgpu_RTA_array_at_end_of_struct) { - std::string str = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Fragment %func "func" - OpExecutionMode %func OriginUpperLeft - OpDecorate %array_t ArrayStride 4 - OpMemberDecorate %struct_t 0 Offset 0 - OpMemberDecorate %struct_t 1 Offset 4 - OpDecorate %struct_t Block - %uint_t = OpTypeInt 32 0 - %array_t = OpTypeRuntimeArray %uint_t - %struct_t = OpTypeStruct %uint_t %array_t -%struct_ptr = OpTypePointer StorageBuffer %struct_t - %2 = OpVariable %struct_ptr StorageBuffer - %void = OpTypeVoid - %func_t = OpTypeFunction %void - %func = OpFunction %void None %func_t - %1 = OpLabel - OpReturn - OpFunctionEnd +TEST_F(ValidateData, TypeForwardReference) { + std::string test = R"( +OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpTypeForwardPointer %1 PhysicalStorageBuffer +%2 = OpTypeStruct +%3 = OpTypeRuntimeArray %1 +%1 = OpTypePointer PhysicalStorageBuffer %2 )"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); + CompileSuccessfully(test, SPV_ENV_UNIVERSAL_1_5); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); } -TEST_F(ValidateData, webgpu_RTA_not_at_end_of_struct) { - std::string str = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Fragment %func "func" - OpExecutionMode %func OriginUpperLeft - OpDecorate %array_t ArrayStride 4 - OpMemberDecorate %struct_t 0 Offset 0 - OpMemberDecorate %struct_t 1 Offset 4 - OpDecorate %struct_t Block - %uint_t = OpTypeInt 32 0 - %array_t = OpTypeRuntimeArray %uint_t - %struct_t = OpTypeStruct %array_t %uint_t -%struct_ptr = OpTypePointer StorageBuffer %struct_t - %2 = OpVariable %struct_ptr StorageBuffer - %void = OpTypeVoid - %func_t = OpTypeFunction %void - %func = OpFunction %void None %func_t - %1 = OpLabel - OpReturn - OpFunctionEnd +TEST_F(ValidateData, VulkanTypeForwardStorageClass) { + std::string test = R"( +OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpMemoryModel Logical GLSL450 +OpTypeForwardPointer %1 Uniform +%2 = OpTypeStruct +%3 = OpTypeRuntimeArray %1 +%1 = OpTypePointer Uniform %2 )"; - CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + CompileSuccessfully(test, SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("In WebGPU, OpTypeRuntimeArray must only be used for " - "the last member of an OpTypeStruct\n %_struct_3 = " - "OpTypeStruct %_runtimearr_uint %uint\n")); + AnyVUID("VUID-StandaloneSpirv-OpTypeForwardPointer-04711")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Vulkan, OpTypeForwardPointer must have " + "a storage class of PhysicalStorageBuffer.")); +} + +TEST_F(ValidateData, TypeForwardReferenceMustBeForwardPointer) { + std::string test = R"( +OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeStruct +%2 = OpTypeRuntimeArray %3 +%3 = OpTypePointer PhysicalStorageBuffer %1 +)"; + + CompileSuccessfully(test, SPV_ENV_UNIVERSAL_1_5); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 3[%_ptr_PhysicalStorageBuffer__struct_1] " + "requires a previous definition")); } } // namespace diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index e6461624..cbf6d7c1 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -42,8 +42,8 @@ struct TestResult { }; using ValidateDecorations = spvtest::ValidateBase<bool>; -using ValidateWebGPUCombineDecorationResult = - spvtest::ValidateBase<std::tuple<const char*, TestResult>>; +using ValidateVulkanCombineDecorationResult = + spvtest::ValidateBase<std::tuple<const char*, const char*, TestResult>>; TEST_F(ValidateDecorations, ValidateOpDecorateRegistration) { std::string spirv = R"( @@ -163,44 +163,6 @@ TEST_F(ValidateDecorations, ValidateGroupDecorateRegistration) { EXPECT_THAT(vstate_->id_decorations(4), Eq(expected_decorations)); } -TEST_F(ValidateDecorations, WebGPUOpDecorationGroupBad) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpDecorate %1 DescriptorSet 0 - OpDecorate %1 NonWritable - OpDecorate %1 Restrict - %1 = OpDecorationGroup - OpGroupDecorate %1 %2 %3 - OpGroupDecorate %1 %4 - %float = OpTypeFloat 32 -%_runtimearr_float = OpTypeRuntimeArray %float - %_struct_9 = OpTypeStruct %_runtimearr_float -%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 - %2 = OpVariable %_ptr_Uniform__struct_9 Uniform - %_struct_10 = OpTypeStruct %_runtimearr_float -%_ptr_Uniform__struct_10 = OpTypePointer Uniform %_struct_10 - %3 = OpVariable %_ptr_Uniform__struct_10 Uniform - %_struct_11 = OpTypeStruct %_runtimearr_float -%_ptr_Uniform__struct_11 = OpTypePointer Uniform %_struct_11 - %4 = OpVariable %_ptr_Uniform__struct_11 Uniform - )"; - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpDecorationGroup is not allowed in the WebGPU " - "execution environment.\n %1 = OpDecorationGroup\n")); -} - -// For WebGPU, OpGroupDecorate does not have a test case, because it requires -// being preceded by OpDecorationGroup, which will cause a validation error. - -// For WebGPU, OpGroupMemberDecorate does not have a test case, because it -// requires being preceded by OpDecorationGroup, which will cause a validation -// error. - TEST_F(ValidateDecorations, ValidateGroupMemberDecorateRegistration) { std::string spirv = R"( OpCapability Shader @@ -4679,6 +4641,95 @@ OpFunctionEnd "Object operand of an OpStore.")); } +TEST_F(ValidateDecorations, VulkanFPRoundingModeGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability StorageBuffer16BitAccess + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorate %ssbo 0 Offset 0 + OpDecorate %ssbo Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %17 FPRoundingMode RTE + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstant %float 1 + %half = OpTypeFloat 16 + %ssbo = OpTypeStruct %half +%_ptr_StorageBuffer_ssbo = OpTypePointer StorageBuffer %ssbo + %_ = OpVariable %_ptr_StorageBuffer_ssbo StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_half = OpTypePointer StorageBuffer %half + %main = OpFunction %void None %3 + %5 = OpLabel + %b = OpVariable %_ptr_Function_float Function + OpStore %b %float_1 + %16 = OpLoad %float %b + %17 = OpFConvert %half %16 + %19 = OpAccessChain %_ptr_StorageBuffer_half %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateDecorations, VulkanFPRoundingModeBadMode) { + std::string spirv = R"( + OpCapability Shader + OpCapability StorageBuffer16BitAccess + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorate %ssbo 0 Offset 0 + OpDecorate %ssbo Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %17 FPRoundingMode RTP + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstant %float 1 + %half = OpTypeFloat 16 + %ssbo = OpTypeStruct %half +%_ptr_StorageBuffer_ssbo = OpTypePointer StorageBuffer %ssbo + %_ = OpVariable %_ptr_StorageBuffer_ssbo StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_half = OpTypePointer StorageBuffer %half + %main = OpFunction %void None %3 + %5 = OpLabel + %b = OpVariable %_ptr_Function_float Function + OpStore %b %float_1 + %16 = OpLoad %float %b + %17 = OpFConvert %half %16 + %19 = OpAccessChain %_ptr_StorageBuffer_half %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-FPRoundingMode-04675")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In Vulkan, the FPRoundingMode mode must only by RTE or RTZ.")); +} + TEST_F(ValidateDecorations, GroupDecorateTargetsDecorationGroup) { std::string spirv = R"( OpCapability Shader @@ -6299,11 +6350,12 @@ TEST_F(ValidateDecorations, NonWritableRuntimeArrayGood) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_P(ValidateWebGPUCombineDecorationResult, Decorate) { +TEST_P(ValidateVulkanCombineDecorationResult, Decorate) { const char* const decoration = std::get<0>(GetParam()); - const TestResult& test_result = std::get<1>(GetParam()); + const char* const vuid = std::get<1>(GetParam()); + const TestResult& test_result = std::get<2>(GetParam()); - CodeGenerator generator = CodeGenerator::GetWebGPUShaderCodeGenerator(); + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = "OpDecorate %u32 "; generator.before_types_ += decoration; generator.before_types_ += "\n"; @@ -6313,52 +6365,24 @@ TEST_P(ValidateWebGPUCombineDecorationResult, Decorate) { entry_point.execution_model = "Vertex"; generator.entry_points_.push_back(std::move(entry_point)); - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); - ASSERT_EQ(test_result.validation_result, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - if (test_result.error_str != "") { - EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); - } -} - -TEST_P(ValidateWebGPUCombineDecorationResult, DecorateMember) { - const char* const decoration = std::get<0>(GetParam()); - const TestResult& test_result = std::get<1>(GetParam()); - - CodeGenerator generator = CodeGenerator::GetWebGPUShaderCodeGenerator(); - generator.before_types_ = "OpMemberDecorate %struct_type 0 "; - generator.before_types_ += decoration; - generator.before_types_ += "\n"; - - generator.after_types_ = "%struct_type = OpTypeStruct %u32\n"; - - EntryPoint entry_point; - entry_point.name = "main"; - entry_point.execution_model = "Vertex"; - generator.entry_points_.push_back(std::move(entry_point)); - - CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); ASSERT_EQ(test_result.validation_result, - ValidateInstructions(SPV_ENV_WEBGPU_0)); + ValidateInstructions(SPV_ENV_VULKAN_1_0)); if (!test_result.error_str.empty()) { EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } } INSTANTIATE_TEST_SUITE_P( - DecorationCapabilityFailure, ValidateWebGPUCombineDecorationResult, - Combine(Values("CPacked", "Patch", "Sample", "Constant", - "SaturatedConversion", "NonUniformEXT"), - Values(TestResult(SPV_ERROR_INVALID_CAPABILITY, - "requires one of these capabilities")))); - -INSTANTIATE_TEST_SUITE_P( - DecorationAllowListFailure, ValidateWebGPUCombineDecorationResult, - Combine(Values("RelaxedPrecision", "BufferBlock", "GLSLShared", - "GLSLPacked", "Invariant", "Volatile", "Coherent"), + DecorationAllowListFailure, ValidateVulkanCombineDecorationResult, + Combine(Values("GLSLShared", "GLSLPacked"), + Values("VUID-StandaloneSpirv-GLSLShared-04669"), Values(TestResult( SPV_ERROR_INVALID_ID, - "is not valid for the WebGPU execution environment.")))); + "is not valid for the Vulkan execution environment.")))); TEST_F(ValidateDecorations, NonWritableVarFunctionV13Bad) { std::string spirv = ShaderWithNonWritableTarget("%var_func"); @@ -7159,6 +7183,469 @@ OpDecorate %float Location 0 "or member of a structure type")); } +TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 8 1 1 + OpMemberDecorate %first 0 Offset 0 + OpDecorate %first Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %first = OpTypeStruct %int +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 + OpStore %13 %int_2 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateDecorations, WorkgroupBlockVariableRequiresV14) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 8 1 1 + OpMemberDecorate %first 0 Offset 0 + OpDecorate %first Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %first = OpTypeStruct %int +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 + OpStore %13 %int_2 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_WRONG_VERSION, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires SPIR-V version 1.4 or later")); +} + +TEST_F(ValidateDecorations, WorkgroupSingleNonBlockVariable) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %a + OpExecutionMode %main LocalSize 8 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %a = OpVariable %_ptr_Workgroup_int Workgroup + %int_2 = OpConstant %int 2 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %a %int_2 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateDecorations, WorkgroupMultiBlockVariable) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ %__0 + OpExecutionMode %main LocalSize 8 1 1 + OpMemberDecorate %first 0 Offset 0 + OpDecorate %first Block + OpMemberDecorate %second 0 Offset 0 + OpDecorate %second Block + OpDecorate %_ Aliased + OpDecorate %__0 Aliased + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %first = OpTypeStruct %int +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %second = OpTypeStruct %int +%_ptr_Workgroup_second = OpTypePointer Workgroup %second + %__0 = OpVariable %_ptr_Workgroup_second Workgroup + %int_3 = OpConstant %int 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 + OpStore %13 %int_2 + %18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0 + OpStore %18 %int_3 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateDecorations, WorkgroupBlockVariableWith8BitType) { + std::string spirv = R"( + OpCapability Shader + OpCapability Int8 + OpCapability WorkgroupMemoryExplicitLayout8BitAccessKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 2 1 1 + OpMemberDecorate %first 0 Offset 0 + OpDecorate %first Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %char = OpTypeInt 8 1 + %first = OpTypeStruct %char +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %char_2 = OpConstant %char 2 +%_ptr_Workgroup_char = OpTypePointer Workgroup %char + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpAccessChain %_ptr_Workgroup_char %_ %int_0 + OpStore %14 %char_2 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateDecorations, WorkgroupMultiNonBlockVariable) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %a %b + OpExecutionMode %main LocalSize 8 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %a = OpVariable %_ptr_Workgroup_int Workgroup + %int_2 = OpConstant %int 2 + %b = OpVariable %_ptr_Workgroup_int Workgroup + %int_3 = OpConstant %int 3 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %a %int_2 + OpStore %b %int_3 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateDecorations, WorkgroupBlockVariableWith16BitType) { + std::string spirv = R"( + OpCapability Shader + OpCapability Float16 + OpCapability Int16 + OpCapability WorkgroupMemoryExplicitLayout16BitAccessKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 2 1 1 + OpMemberDecorate %first 0 Offset 0 + OpMemberDecorate %first 1 Offset 2 + OpDecorate %first Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %short = OpTypeInt 16 1 + %half = OpTypeFloat 16 + %first = OpTypeStruct %short %half +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %short_3 = OpConstant %short 3 +%_ptr_Workgroup_short = OpTypePointer Workgroup %short + %int_1 = OpConstant %int 1 +%half_0x1_898p_3 = OpConstant %half 0x1.898p+3 +%_ptr_Workgroup_half = OpTypePointer Workgroup %half + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpAccessChain %_ptr_Workgroup_short %_ %int_0 + OpStore %15 %short_3 + %19 = OpAccessChain %_ptr_Workgroup_half %_ %int_1 + OpStore %19 %half_0x1_898p_3 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateDecorations, WorkgroupBlockVariableScalarLayout) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %B + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpMemberDecorate %S 2 Offset 16 + OpMemberDecorate %S 3 Offset 28 + OpDecorate %S Block + OpDecorate %B Aliased + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float %v3float %v3float +%_ptr_Workgroup_S = OpTypePointer Workgroup %S + %B = OpVariable %_ptr_Workgroup_S Workgroup + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + spvValidatorOptionsSetWorkgroupScalarBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, WorkgroupMixBlockAndNonBlockBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ %b + OpExecutionMode %main LocalSize 8 1 1 + OpMemberDecorate %first 0 Offset 0 + OpDecorate %first Block + OpDecorate %_ Aliased + OpDecorate %b Aliased + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %first = OpTypeStruct %int +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %b = OpVariable %_ptr_Workgroup_int Workgroup + %int_3 = OpConstant %int 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 + OpStore %13 %int_2 + OpStore %b %int_3 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("either all or none of the Workgroup Storage Class variables " + "in the entry point interface must point to struct types " + "decorated with Block")); +} + +TEST_F(ValidateDecorations, WorkgroupMultiBlockVariableMissingAliased) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ %__0 + OpExecutionMode %main LocalSize 8 1 1 + OpMemberDecorate %first 0 Offset 0 + OpDecorate %first Block + OpMemberDecorate %second 0 Offset 0 + OpDecorate %second Block + OpDecorate %_ Aliased + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %first = OpTypeStruct %int +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %second = OpTypeStruct %int +%_ptr_Workgroup_second = OpTypePointer Workgroup %second + %__0 = OpVariable %_ptr_Workgroup_second Workgroup + %int_3 = OpConstant %int 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 + OpStore %13 %int_2 + %18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0 + OpStore %18 %int_3 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("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")); +} + +TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableNotAStruct) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 8 1 1 + OpDecorate %first Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %first = OpTypeArray %int %int_3 +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 + OpStore %13 %int_2 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Block decoration on a non-struct type")); +} + +TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 8 1 1 + OpDecorate %first Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %first = OpTypeStruct %int +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 + OpStore %13 %int_2 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Block must be explicitly laid out with Offset decorations")); +} + +TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) { + std::string spirv = R"( + OpCapability Shader + OpCapability WorkgroupMemoryExplicitLayoutKHR + OpExtension "SPV_KHR_workgroup_memory_explicit_layout" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 8 1 1 + OpMemberDecorate %first 0 Offset 1 + OpDecorate %first Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %first = OpTypeStruct %int +%_ptr_Workgroup_first = OpTypePointer Workgroup %first + %_ = OpVariable %_ptr_Workgroup_first Workgroup + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_ptr_Workgroup_int = OpTypePointer Workgroup %int + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0 + OpStore %13 %int_2 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Block for variable in Workgroup storage class must follow " + "standard storage buffer layout rules: " + "member 0 at offset 1 is not aligned to 4")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_fixtures.h b/test/val/val_fixtures.h index 5635c781..acbe0e57 100644 --- a/test/val/val_fixtures.h +++ b/test/val/val_fixtures.h @@ -183,4 +183,43 @@ spv_position_t ValidateBase<T>::getErrorPosition() { } // namespace spvtest +// For Vulkan testing. +// Allows test parameter test to list all possible VUIDs with a delimiter that +// is then split here to check if one VUID was in the error message +MATCHER_P(AnyVUID, vuid_set, "VUID from the set is in error message") { + // use space as delimiter because clang-format will properly line break VUID + // strings which is important the entire VUID is in a single line for script + // to scan + std::string delimiter = " "; + std::string token; + std::string vuids = std::string(vuid_set); + size_t position; + + // Catch case were someone accidentally left spaces by trimming string + // clang-format off + vuids.erase(std::find_if(vuids.rbegin(), vuids.rend(), [](unsigned char c) { + return (c != ' '); + }).base(), vuids.end()); + vuids.erase(vuids.begin(), std::find_if(vuids.begin(), vuids.end(), [](unsigned char c) { + return (c != ' '); + })); + // clang-format on + + do { + position = vuids.find(delimiter); + if (position != std::string::npos) { + token = vuids.substr(0, position); + vuids.erase(0, position + delimiter.length()); + } else { + token = vuids.substr(0); // last item + } + + // arg contains diagnostic message + if (arg.find(token) != std::string::npos) { + return true; + } + } while (position != std::string::npos); + return false; +} + #endif // TEST_VAL_VAL_FIXTURES_H_ diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index 069e8f20..c65d1715 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp @@ -411,10 +411,10 @@ TEST_F(ValidateIdWithMessage, OpEntryPointFunctionBad) { } TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBad) { std::string spirv = kGLSL450MemoryModel + R"( - OpEntryPoint GLCompute %3 "" -%1 = OpTypeVoid -%2 = OpTypeFunction %1 %1 -%3 = OpFunction %1 None %2 + OpEntryPoint GLCompute %1 "" +%2 = OpTypeVoid +%3 = OpTypeFunction %2 %2 +%1 = OpFunction %2 None %3 %4 = OpLabel OpReturn OpFunctionEnd)"; @@ -426,11 +426,11 @@ TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBad) { } TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBad) { std::string spirv = kGLSL450MemoryModel + R"( - OpEntryPoint GLCompute %3 "" -%1 = OpTypeInt 32 0 -%ret = OpConstant %1 0 -%2 = OpTypeFunction %1 -%3 = OpFunction %1 None %2 + OpEntryPoint GLCompute %1 "" +%2 = OpTypeInt 32 0 +%ret = OpConstant %2 0 +%3 = OpTypeFunction %2 +%1 = OpFunction %2 None %3 %4 = OpLabel OpReturnValue %ret OpFunctionEnd)"; @@ -440,6 +440,45 @@ TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBad) { HasSubstr("OpEntryPoint Entry Point <id> '1[%1]'s function " "return type is not void.")); } +TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBadInVulkan) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "" +%2 = OpTypeVoid +%3 = OpTypeFunction %2 %2 +%1 = OpFunction %2 None %3 +%4 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04633")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpEntryPoint Entry Point <id> '1[%1]'s function " + "parameter count is not zero")); +} +TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBadInVulkan) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "" +%2 = OpTypeInt 32 0 +%ret = OpConstant %2 0 +%3 = OpTypeFunction %2 +%1 = OpFunction %2 None %3 +%4 = OpLabel + OpReturnValue %ret + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04633")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpEntryPoint Entry Point <id> '1[%1]'s function " + "return type is not void.")); +} TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) { std::string spirv = R"( @@ -915,31 +954,6 @@ TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) { } } -TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InWebGPU) { - env_ = SPV_ENV_WEBGPU_0; - const int width = GetParam(); - // WebGPU only has 32 bit integers. - if (width != 32) return; - const int max_int_width = 32; - const auto module = CompileSuccessfully(MakeArrayLength( - big_num_ending_0(width), kUnsigned, width, max_int_width, true)); - EXPECT_EQ(SPV_SUCCESS, Val(module)); -} - -TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InWebGPU) { - env_ = SPV_ENV_WEBGPU_0; - const int width = GetParam(); - // WebGPU only has 32 bit integers. - if (width != 32) return; - const int max_int_width = 32; - const auto module = CompileSuccessfully(MakeArrayLength( - big_num_ending_1(width), kUnsigned, width, max_int_width, true)); - EXPECT_EQ(SPV_ERROR_INVALID_ID, - Val(module, - "OpTypeArray Length <id> '3\\[%.*\\]' size exceeds max value " - "2147483648 permitted by WebGPU: got 2147483649")); -} - // The only valid widths for integers are 8, 16, 32, and 64. // Since the Int8 capability requires the Kernel capability, and the Kernel // capability prohibits usage of signed integers, we can skip 8-bit integers @@ -4692,39 +4706,6 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleLiterals) { "size of 5.")); } -TEST_F(ValidateIdWithMessage, WebGPUOpVectorShuffle0xFFFFFFFFLiteralBad) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR -%float = OpTypeFloat 32 -%vec2 = OpTypeVector %float 2 -%vec3 = OpTypeVector %float 3 -%vec4 = OpTypeVector %float 4 -%ptr_vec2 = OpTypePointer Function %vec2 -%ptr_vec3 = OpTypePointer Function %vec3 -%float_1 = OpConstant %float 1 -%float_2 = OpConstant %float 2 -%1 = OpConstantComposite %vec2 %float_2 %float_1 -%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 -%3 = OpTypeFunction %vec4 -%4 = OpFunction %vec4 None %3 -%5 = OpLabel -%var = OpVariable %ptr_vec2 Function %1 -%var2 = OpVariable %ptr_vec3 Function %2 -%6 = OpLoad %vec2 %var -%7 = OpLoad %vec3 %var2 -%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff - OpReturnValue %8 - OpFunctionEnd)"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component literal at operand 3 cannot be 0xFFFFFFFF in" - " WebGPU execution environment.")); -} - // TODO: OpCompositeConstruct // TODO: OpCompositeExtract // TODO: OpCompositeInsert diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index 2e84a29f..6a0333cd 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -66,9 +66,9 @@ OpCapability ImageBuffer %uniform_image_f32_1d_0001 %uniform_image_f32_1d_0002_rgba32f %uniform_image_f32_2d_0001 -%uniform_image_f32_2d_0010 +%uniform_image_f32_2d_0011 ; multisampled sampled %uniform_image_u32_2d_0001 -%uniform_image_u32_2d_0000 +%uniform_image_u32_2d_0002 %uniform_image_s32_3d_0001 %uniform_image_f32_2d_0002 %uniform_image_s32_2d_0002 @@ -80,6 +80,7 @@ OpCapability ImageBuffer %private_image_u32_buffer_0002_r32ui %private_image_u32_spd_0002 %private_image_f32_buffer_0002_r32ui +%input_flat_u32 )"; ss << capabilities_and_extensions; @@ -99,12 +100,12 @@ OpDecorate %uniform_image_f32_1d_0002_rgba32f DescriptorSet 0 OpDecorate %uniform_image_f32_1d_0002_rgba32f Binding 1 OpDecorate %uniform_image_f32_2d_0001 DescriptorSet 0 OpDecorate %uniform_image_f32_2d_0001 Binding 2 -OpDecorate %uniform_image_f32_2d_0010 DescriptorSet 0 -OpDecorate %uniform_image_f32_2d_0010 Binding 3 +OpDecorate %uniform_image_f32_2d_0011 DescriptorSet 0 +OpDecorate %uniform_image_f32_2d_0011 Binding 3 OpDecorate %uniform_image_u32_2d_0001 DescriptorSet 1 OpDecorate %uniform_image_u32_2d_0001 Binding 0 -OpDecorate %uniform_image_u32_2d_0000 DescriptorSet 1 -OpDecorate %uniform_image_u32_2d_0000 Binding 1 +OpDecorate %uniform_image_u32_2d_0002 DescriptorSet 1 +OpDecorate %uniform_image_u32_2d_0002 Binding 1 OpDecorate %uniform_image_s32_3d_0001 DescriptorSet 1 OpDecorate %uniform_image_s32_3d_0001 Binding 2 OpDecorate %uniform_image_f32_2d_0002 DescriptorSet 1 @@ -121,6 +122,8 @@ OpDecorate %uniform_image_f32_cube_0102_rgba32f DescriptorSet 2 OpDecorate %uniform_image_f32_cube_0102_rgba32f Binding 3 OpDecorate %uniform_sampler DescriptorSet 3 OpDecorate %uniform_sampler Binding 0 +OpDecorate %input_flat_u32 Flat +OpDecorate %input_flat_u32 Location 0 )"; } @@ -133,6 +136,7 @@ OpDecorate %uniform_sampler Binding 0 %u32 = OpTypeInt 32 0 %s32 = OpTypeInt 32 1 %u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 %s32vec2 = OpTypeVector %s32 2 %u32vec2 = OpTypeVector %u32 2 %f32vec2 = OpTypeVector %f32 2 @@ -223,27 +227,25 @@ OpDecorate %uniform_sampler Binding 0 %type_image_f32_1d_0002_rgba32f = OpTypeImage %f32 1D 0 0 0 2 Rgba32f %ptr_image_f32_1d_0002_rgba32f = OpTypePointer UniformConstant %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f = OpVariable %ptr_image_f32_1d_0002_rgba32f UniformConstant -%type_sampled_image_f32_1d_0002_rgba32f = OpTypeSampledImage %type_image_f32_1d_0002_rgba32f %type_image_f32_2d_0001 = OpTypeImage %f32 2D 0 0 0 1 Unknown %ptr_image_f32_2d_0001 = OpTypePointer UniformConstant %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 = OpVariable %ptr_image_f32_2d_0001 UniformConstant %type_sampled_image_f32_2d_0001 = OpTypeSampledImage %type_image_f32_2d_0001 -%type_image_f32_2d_0010 = OpTypeImage %f32 2D 0 0 1 0 Unknown -%ptr_image_f32_2d_0010 = OpTypePointer UniformConstant %type_image_f32_2d_0010 -%uniform_image_f32_2d_0010 = OpVariable %ptr_image_f32_2d_0010 UniformConstant -%type_sampled_image_f32_2d_0010 = OpTypeSampledImage %type_image_f32_2d_0010 +%type_image_f32_2d_0011 = OpTypeImage %f32 2D 0 0 1 1 Unknown +%ptr_image_f32_2d_0011 = OpTypePointer UniformConstant %type_image_f32_2d_0011 +%uniform_image_f32_2d_0011 = OpVariable %ptr_image_f32_2d_0011 UniformConstant +%type_sampled_image_f32_2d_0011 = OpTypeSampledImage %type_image_f32_2d_0011 %type_image_u32_2d_0001 = OpTypeImage %u32 2D 0 0 0 1 Unknown %ptr_image_u32_2d_0001 = OpTypePointer UniformConstant %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 = OpVariable %ptr_image_u32_2d_0001 UniformConstant %type_sampled_image_u32_2d_0001 = OpTypeSampledImage %type_image_u32_2d_0001 -%type_image_u32_2d_0000 = OpTypeImage %u32 2D 0 0 0 0 Unknown -%ptr_image_u32_2d_0000 = OpTypePointer UniformConstant %type_image_u32_2d_0000 -%uniform_image_u32_2d_0000 = OpVariable %ptr_image_u32_2d_0000 UniformConstant -%type_sampled_image_u32_2d_0000 = OpTypeSampledImage %type_image_u32_2d_0000 +%type_image_u32_2d_0002 = OpTypeImage %u32 2D 0 0 0 2 Unknown +%ptr_image_u32_2d_0002 = OpTypePointer UniformConstant %type_image_u32_2d_0002 +%uniform_image_u32_2d_0002 = OpVariable %ptr_image_u32_2d_0002 UniformConstant %type_image_s32_3d_0001 = OpTypeImage %s32 3D 0 0 0 1 Unknown %ptr_image_s32_3d_0001 = OpTypePointer UniformConstant %type_image_s32_3d_0001 @@ -253,17 +255,14 @@ OpDecorate %uniform_sampler Binding 0 %type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Unknown %ptr_image_f32_2d_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant -%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002 %type_image_s32_2d_0002 = OpTypeImage %s32 2D 0 0 0 2 Unknown %ptr_image_s32_2d_0002 = OpTypePointer UniformConstant %type_image_s32_2d_0002 %uniform_image_s32_2d_0002 = OpVariable %ptr_image_s32_2d_0002 UniformConstant -%type_sampled_image_s32_2d_0002 = OpTypeSampledImage %type_image_s32_2d_0002 %type_image_f32_spd_0002 = OpTypeImage %f32 SubpassData 0 0 0 2 Unknown %ptr_image_f32_spd_0002 = OpTypePointer UniformConstant %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 = OpVariable %ptr_image_f32_spd_0002 UniformConstant -%type_sampled_image_f32_spd_0002 = OpTypeSampledImage %type_image_f32_spd_0002 %type_image_f32_3d_0111 = OpTypeImage %f32 3D 0 1 1 1 Unknown %ptr_image_f32_3d_0111 = OpTypePointer UniformConstant %type_image_f32_3d_0111 @@ -278,7 +277,6 @@ OpDecorate %uniform_sampler Binding 0 %type_image_f32_cube_0102_rgba32f = OpTypeImage %f32 Cube 0 1 0 2 Rgba32f %ptr_image_f32_cube_0102_rgba32f = OpTypePointer UniformConstant %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f = OpVariable %ptr_image_f32_cube_0102_rgba32f UniformConstant -%type_sampled_image_f32_cube_0102_rgba32f = OpTypeSampledImage %type_image_f32_cube_0102_rgba32f %type_sampler = OpTypeSampler %ptr_sampler = OpTypePointer UniformConstant %type_sampler @@ -299,6 +297,9 @@ OpDecorate %uniform_sampler Binding 0 %ptr_Image_f32 = OpTypePointer Image %f32 %ptr_image_f32_buffer_0002_r32ui = OpTypePointer Private %type_image_f32_buffer_0002_r32ui %private_image_f32_buffer_0002_r32ui = OpVariable %ptr_image_f32_buffer_0002_r32ui Private + +%ptr_input_flat_u32 = OpTypePointer Input %u32 +%input_flat_u32 = OpVariable %ptr_input_flat_u32 Input )"; if (env == SPV_ENV_UNIVERSAL_1_0) { @@ -311,7 +312,6 @@ OpDecorate %uniform_sampler Binding 0 %type_image_void_2d_0002 = OpTypeImage %void 2D 0 0 0 2 Unknown %ptr_image_void_2d_0002 = OpTypePointer UniformConstant %type_image_void_2d_0002 %uniform_image_void_2d_0002 = OpVariable %ptr_image_void_2d_0002 UniformConstant -%type_sampled_image_void_2d_0002 = OpTypeSampledImage %type_image_void_2d_0002 %type_image_f32_rect_0001 = OpTypeImage %f32 Rect 0 0 0 1 Unknown %ptr_image_f32_rect_0001 = OpTypePointer UniformConstant %type_image_f32_rect_0001 @@ -400,15 +400,15 @@ OpMemoryModel Physical32 OpenCL %uniform_image_f32_2d_0001 = OpVariable %ptr_image_f32_2d_0001 UniformConstant %type_sampled_image_f32_2d_0001 = OpTypeSampledImage %type_image_f32_2d_0001 -%type_image_f32_2d_0010 = OpTypeImage %f32 2D 0 0 1 0 Unknown -%ptr_image_f32_2d_0010 = OpTypePointer UniformConstant %type_image_f32_2d_0010 -%uniform_image_f32_2d_0010 = OpVariable %ptr_image_f32_2d_0010 UniformConstant -%type_sampled_image_f32_2d_0010 = OpTypeSampledImage %type_image_f32_2d_0010 +%type_image_f32_2d_0011 = OpTypeImage %f32 2D 0 0 1 1 Unknown +%ptr_image_f32_2d_0011 = OpTypePointer UniformConstant %type_image_f32_2d_0011 +%uniform_image_f32_2d_0011 = OpVariable %ptr_image_f32_2d_0011 UniformConstant +%type_sampled_image_f32_2d_0011 = OpTypeSampledImage %type_image_f32_2d_0011 -%type_image_f32_3d_0010 = OpTypeImage %f32 3D 0 0 1 0 Unknown -%ptr_image_f32_3d_0010 = OpTypePointer UniformConstant %type_image_f32_3d_0010 -%uniform_image_f32_3d_0010 = OpVariable %ptr_image_f32_3d_0010 UniformConstant -%type_sampled_image_f32_3d_0010 = OpTypeSampledImage %type_image_f32_3d_0010 +%type_image_f32_3d_0011 = OpTypeImage %f32 3D 0 0 1 1 Unknown +%ptr_image_f32_3d_0011 = OpTypePointer UniformConstant %type_image_f32_3d_0011 +%uniform_image_f32_3d_0011 = OpVariable %ptr_image_f32_3d_0011 UniformConstant +%type_sampled_image_f32_3d_0011 = OpTypeSampledImage %type_image_f32_3d_0011 %type_image_f32_rect_0001 = OpTypeImage %f32 Rect 0 0 0 1 Unknown %ptr_image_f32_rect_0001 = OpTypePointer UniformConstant %type_image_f32_rect_0001 @@ -431,12 +431,35 @@ OpFunctionEnd)"; return ss.str(); } +std::string GetKernelHeader() { + return R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Physical32 OpenCL + %void = OpTypeVoid + %func = OpTypeFunction %void + %f32 = OpTypeFloat 32 + %u32 = OpTypeInt 32 0 + )"; +} + +std::string TrivialMain() { + return R"( + %main = OpFunction %void None %func + %entry = OpLabel + OpReturn + OpFunctionEnd + )"; +} + std::string GetShaderHeader(const std::string& capabilities_and_extensions = "", bool include_entry_point = true) { std::ostringstream ss; ss << R"( OpCapability Shader OpCapability Int64 +OpCapability Float64 )"; ss << capabilities_and_extensions; @@ -457,9 +480,11 @@ OpMemoryModel Logical GLSL450 %func = OpTypeFunction %void %bool = OpTypeBool %f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 %u32 = OpTypeInt 32 0 %u64 = OpTypeInt 64 0 %s32 = OpTypeInt 32 1 +%s64 = OpTypeInt 64 1 )"; return ss.str(); @@ -481,8 +506,7 @@ TEST_F(ValidateImage, TypeImageWrongSampledType) { TEST_F(ValidateImage, TypeImageVoidSampledTypeVulkan) { const std::string code = GetShaderHeader() + R"( %img_type = OpTypeImage %void 2D 0 0 0 1 Unknown -%void_func = OpTypeFunction %void -%main = OpFunction %void None %void_func +%main = OpFunction %void None %func %main_lab = OpLabel OpReturn OpFunctionEnd @@ -492,15 +516,153 @@ OpFunctionEnd CompileSuccessfully(code, env); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Sampled Type to be a 32-bit int " - "or float scalar type for Vulkan environment")); + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-04656")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sampled Type to be a 32-bit int, 64-bit int " + "or 32-bit float scalar type for Vulkan environment")); } -TEST_F(ValidateImage, TypeImageU64SampledTypeVulkan) { +TEST_F(ValidateImage, TypeImageU32SampledTypeVulkan) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %u32 2D 0 0 0 1 Unknown +%main = OpFunction %void None %func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImageI32SampledTypeVulkan) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %s32 2D 0 0 0 1 Unknown +%main = OpFunction %void None %func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImageI64SampledTypeNoCapabilityVulkan) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %s64 2D 0 0 0 1 Unknown +%main = OpFunction %void None %func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Int64ImageEXT is required when using " + "Sampled Type of 64-bit int")); +} + +TEST_F(ValidateImage, TypeImageI64SampledTypeVulkan) { + const std::string code = GetShaderHeader( + "OpCapability Int64ImageEXT\nOpExtension " + "\"SPV_EXT_shader_image_int64\"\n") + + R"( +%img_type = OpTypeImage %s64 2D 0 0 0 1 Unknown +%main = OpFunction %void None %func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImageU64SampledTypeNoCapabilityVulkan) { const std::string code = GetShaderHeader() + R"( %img_type = OpTypeImage %u64 2D 0 0 0 1 Unknown -%void_func = OpTypeFunction %void -%main = OpFunction %void None %void_func +%main = OpFunction %void None %func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Int64ImageEXT is required when using " + "Sampled Type of 64-bit int")); +} + +TEST_F(ValidateImage, TypeImageU64SampledTypeVulkan) { + const std::string code = GetShaderHeader( + "OpCapability Int64ImageEXT\nOpExtension " + "\"SPV_EXT_shader_image_int64\"\n") + + R"( +%img_type = OpTypeImage %u64 2D 0 0 0 1 Unknown +%main = OpFunction %void None %func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImageF32SampledTypeVulkan) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %f32 2D 0 0 0 1 Unknown +%main = OpFunction %void None %func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImageF64SampledTypeVulkan) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %f64 2D 0 0 0 1 Unknown +%main = OpFunction %void None %func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-04656")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sampled Type to be a 32-bit int, 64-bit int " + "or 32-bit float scalar type for Vulkan environment")); +} + +TEST_F(ValidateImage, TypeImageF64SampledTypeWithInt64Vulkan) { + const std::string code = GetShaderHeader( + "OpCapability Int64ImageEXT\nOpExtension " + "\"SPV_EXT_shader_image_int64\"\n") + + R"( +%img_type = OpTypeImage %f64 2D 0 0 0 1 Unknown +%main = OpFunction %void None %func %main_lab = OpLabel OpReturn OpFunctionEnd @@ -510,8 +672,10 @@ OpFunctionEnd CompileSuccessfully(code, env); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Sampled Type to be a 32-bit int " - "or float scalar type for Vulkan environment")); + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-04656")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sampled Type to be a 32-bit int, 64-bit int " + "or 32-bit float scalar type for Vulkan environment")); } TEST_F(ValidateImage, TypeImageWrongDepth) { @@ -571,6 +735,83 @@ TEST_F(ValidateImage, TypeImageWrongSampledForSubpassData) { HasSubstr("Dim SubpassData requires Sampled to be 2")); } +TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) { + const std::string code = GetKernelHeader() + R"( +%img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_1)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImage_OpenCL_Sampled1_Invalid) { + const std::string code = GetKernelHeader() + R"( +%img_type = OpTypeImage %void 2D 0 0 0 1 Unknown ReadOnly +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_2_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled must be 0 in the OpenCL environment.")); +} + +TEST_F(ValidateImage, TypeImage_OpenCL_Sampled2_Invalid) { + const std::string code = GetKernelHeader() + R"( +%img_type = OpTypeImage %void 2D 0 0 0 2 Unknown ReadOnly +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_2_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled must be 0 in the OpenCL environment.")); +} + +TEST_F(ValidateImage, TypeImage_OpenCL_AccessQualifierMissing) { + const std::string code = GetKernelHeader() + R"( +%img_type = OpTypeImage %void 2D 0 0 0 0 Unknown +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_2_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In the OpenCL environment, the optional Access " + "Qualifier must be present")); +} + +TEST_F(ValidateImage, TypeImage_Vulkan_Sampled1_OK) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %f32 2D 0 0 0 1 Unknown +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImage_Vulkan_Sampled2_OK) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %f32 2D 0 0 0 2 Rgba32f +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImage_Vulkan_Sampled0_Invalid) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %f32 2D 0 0 0 0 Unknown +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-04657")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled must be 1 or 2 in the Vulkan environment.")); +} + TEST_F(ValidateImage, TypeImageWrongFormatForSubpassData) { const std::string code = GetShaderHeader("OpCapability InputAttachment\n", false) + @@ -584,7 +825,44 @@ TEST_F(ValidateImage, TypeImageWrongFormatForSubpassData) { HasSubstr("Dim SubpassData requires format Unknown")); } -TEST_F(ValidateImage, TypeSampledImageNotImage) { +TEST_F(ValidateImage, TypeImageMultisampleStorageImage_MissingCapability) { + const std::string code = GetShaderHeader("", false) + + R"( +%img_type = OpTypeImage %f32 2D 0 0 1 2 Rgba32f +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()) << code; + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability StorageImageMultisample is required when " + "using multisampled storage image")); +} + +TEST_F(ValidateImage, TypeImageMultisampleStorageImage_UsesCapability) { + const std::string code = + GetShaderHeader("OpCapability StorageImageMultisample\n", false) + + R"( +%img_type = OpTypeImage %f32 2D 0 0 1 2 Rgba32f +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << code; + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeImageMultisampleSubpassData_OK) { + const std::string code = + GetShaderHeader("OpCapability InputAttachment\n", false) + + R"( +%img_type = OpTypeImage %f32 SubpassData 0 0 1 2 Unknown +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << code; + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, TypeSampledImage_NotImage_Error) { const std::string code = GetShaderHeader("", false) + R"( %simg_type = OpTypeSampledImage %f32 )"; @@ -595,6 +873,43 @@ TEST_F(ValidateImage, TypeSampledImageNotImage) { HasSubstr("Expected Image to be of type OpTypeImage")); } +TEST_F(ValidateImage, TypeSampledImage_Sampled0_Success) { + // This is ok in the OpenCL and universal environments. + // Vulkan will reject an OpTypeImage with Sampled=0, checked elsewhere. + const std::string code = GetShaderHeader() + R"( +%imty = OpTypeImage %f32 2D 0 0 0 0 Unknown +%simg_type = OpTypeSampledImage %imty +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateImage, TypeSampledImage_Sampled2_Error) { + const std::string code = GetShaderHeader() + R"( +%storage_image = OpTypeImage %f32 2D 0 0 0 2 Rgba32f +%simg_type = OpTypeSampledImage %storage_image +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled image type requires an image type with " + "\"Sampled\" operand set to 0 or 1")); +} + +TEST_F(ValidateImage, TypeSampledImage_Sampled1_Success) { + const std::string code = GetShaderHeader() + R"( +%im = OpTypeImage %f32 2D 0 0 0 1 Unknown +%simg_type = OpTypeSampledImage %im +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_EQ(getDiagnosticString(), ""); +} + TEST_F(ValidateImage, SampledImageSuccess) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -646,31 +961,32 @@ TEST_F(ValidateImage, SampledImageNotImage) { } TEST_F(ValidateImage, SampledImageImageNotForSampling) { - const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 -%sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_2d_0002 %img %sampler -)"; + const std::string code = GetShaderHeader() + R"( +%im_ty = OpTypeImage %f32 2D 0 0 0 2 Unknown +%sampler_ty = OpTypeSampler +%sampled_image_ty = OpTypeSampledImage %im_ty ; will fail here first! - CompileSuccessfully(GenerateShaderCode(body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image 'Sampled' parameter to be 0 or 1")); -} +%ptr_im_ty = OpTypePointer UniformConstant %im_ty +%var_im = OpVariable %ptr_im_ty UniformConstant -TEST_F(ValidateImage, SampledImageVulkanUnknownSampled) { - const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 -%sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_u32_2d_0000 %img %sampler +%ptr_sampler_ty = OpTypePointer UniformConstant %sampler_ty +%var_sampler = OpVariable %ptr_sampler_ty UniformConstant + +%main = OpFunction %void None %func +%entry = OpLabel +%im = OpLoad %im_ty %var_im +%sampler = OpLoad %sampler_ty %var_sampler +%sampled_image = OpSampledImage %sampled_image_ty %im %sampler +OpReturn +OpFunctionEnd )"; - const spv_target_env env = SPV_ENV_VULKAN_1_0; - CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env), env); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image 'Sampled' parameter to " - "be 1 for Vulkan environment.")); + HasSubstr("Sampled image type requires an image type with " + "\"Sampled\" operand set to 0 or 1")) + << code; } TEST_F(ValidateImage, SampledImageNotSampler) { @@ -743,7 +1059,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 141[%141] cannot be a " + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 136[%136] cannot be a " "type")); } @@ -801,7 +1117,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageCoordTypeBad) { TEST_F(ValidateImage, ImageTexelPointerImageCoordSizeBad) { const std::string body = R"( -%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %uniform_image_u32_2d_0000 %u32vec3_012 %u32_0 +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %uniform_image_u32_2d_0002 %u32vec3_012 %u32_0 %sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 )"; @@ -902,6 +1218,20 @@ TEST_F(ValidateImage, SampleImplicitLodNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, SampleImplicitLodMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_hh Sample %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampling operation is invalid for multisample image")); +} + TEST_F(ValidateImage, SampleImplicitLodWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -1050,6 +1380,20 @@ TEST_F(ValidateImage, SampleExplicitLodNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, SampleExplicitLodMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Lod|Sample %f32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampling operation is invalid for multisample image")); +} + TEST_F(ValidateImage, SampleExplicitLodWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -1182,19 +1526,6 @@ TEST_F(ValidateImage, LodWrongDim) { "2D, 3D or Cube")); } -TEST_F(ValidateImage, LodMultisampled) { - const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 -%sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_2d_0010 %img %sampler -%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Lod %f32_0)"; - - CompileSuccessfully(GenerateShaderCode(body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operand Lod requires 'MS' parameter to be 0")); -} - TEST_F(ValidateImage, MinLodIncompatible) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -1227,20 +1558,6 @@ TEST_F(ValidateImage, ImplicitLodWithGrad) { "Image Operand Grad can only be used with ExplicitLod opcodes")); } -TEST_F(ValidateImage, SampleImplicitLod3DArrayedMultisampledSuccess) { - const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 -%sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 -%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %s32vec3_012 -%res3 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec3_012 -)"; - - CompileSuccessfully(GenerateShaderCode(body).c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); -} - TEST_F(ValidateImage, SampleImplicitLodCubeArrayedSuccess) { const std::string body = R"( %img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 @@ -1285,20 +1602,6 @@ TEST_F(ValidateImage, SampleImplicitLodBiasWrongDim) { "2D, 3D or Cube")); } -TEST_F(ValidateImage, SampleImplicitLodBiasMultisampled) { - const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 -%sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Bias %f32_0_25 -)"; - - CompileSuccessfully(GenerateShaderCode(body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operand Bias requires 'MS' parameter to be 0")); -} - TEST_F(ValidateImage, SampleExplicitLodGradDxWrongType) { const std::string body = R"( %img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 @@ -1361,20 +1664,6 @@ TEST_F(ValidateImage, SampleExplicitLodGradDyWrongSize) { "Expected Image Operand Grad dy to have 3 components, but given 2")); } -TEST_F(ValidateImage, SampleExplicitLodGradMultisampled) { - const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 -%sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %f32vec3_000 %f32vec3_000 -)"; - - CompileSuccessfully(GenerateShaderCode(body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operand Grad requires 'MS' parameter to be 0")); -} - TEST_F(ValidateImage, SampleImplicitLodConstOffsetCubeDim) { const std::string body = R"( %img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 @@ -1393,10 +1682,10 @@ TEST_F(ValidateImage, SampleImplicitLodConstOffsetCubeDim) { TEST_F(ValidateImage, SampleImplicitLodConstOffsetWrongType) { const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %f32vec3_000 +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_00 ConstOffset %f32vec2_00 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); @@ -1409,26 +1698,26 @@ TEST_F(ValidateImage, SampleImplicitLodConstOffsetWrongType) { TEST_F(ValidateImage, SampleImplicitLodConstOffsetWrongSize) { const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %s32vec2_01 +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_00 ConstOffset %s32vec3_012 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand ConstOffset to have 3 " - "components, but given 2")); + HasSubstr("Expected Image Operand ConstOffset to have 2 " + "components, but given 3")); } TEST_F(ValidateImage, SampleImplicitLodConstOffsetNotConst) { const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler %offset = OpSNegate %s32vec3 %s32vec3_012 -%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %offset +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_00 ConstOffset %offset )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); @@ -1455,10 +1744,10 @@ TEST_F(ValidateImage, SampleImplicitLodOffsetCubeDim) { TEST_F(ValidateImage, SampleImplicitLodOffsetWrongType) { const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %f32vec3_000 +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %f32vec2_00 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); @@ -1470,10 +1759,10 @@ TEST_F(ValidateImage, SampleImplicitLodOffsetWrongType) { TEST_F(ValidateImage, SampleImplicitLodOffsetWrongSize) { const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec2_01 +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec3_012 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); @@ -1481,15 +1770,33 @@ TEST_F(ValidateImage, SampleImplicitLodOffsetWrongSize) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Expected Image Operand Offset to have 3 components, but given 2")); + "Expected Image Operand Offset to have 2 components, but given 3")); +} + +TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Offset-04663")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Offset can only be used with " + "OpImage*Gather operations")); } TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) { const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 %sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset|Offset %s32vec3_012 %s32vec3_012 +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset|Offset %s32vec2_01 %s32vec2_01 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); @@ -1499,6 +1806,24 @@ TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) { "cannot be used together")); } +TEST_F(ValidateImage, SampleImplicitLodVulkanMoreThanOneOffset) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset|Offset %s32vec2_01 %s32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Offset-04662")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets " + "cannot be used together")); +} + TEST_F(ValidateImage, SampleImplicitLodMinLodWrongType) { const std::string body = R"( %img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 @@ -1528,21 +1853,6 @@ TEST_F(ValidateImage, SampleImplicitLodMinLodWrongDim) { "1D, 2D, 3D or Cube")); } -TEST_F(ValidateImage, SampleImplicitLodMinLodMultisampled) { - const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 -%sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler -%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 MinLod %f32_0_25 -)"; - - CompileSuccessfully(GenerateShaderCode(body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Image Operand MinLod requires 'MS' parameter to be 0")); -} - TEST_F(ValidateImage, SampleProjExplicitLodSuccess2D) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -1620,6 +1930,20 @@ TEST_F(ValidateImage, SampleProjExplicitLodNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, SampleProjExplicitLodMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec2_hh Lod|Sample %f32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'MS' parameter to be 0")); +} + TEST_F(ValidateImage, SampleProjExplicitLodWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -1741,6 +2065,20 @@ TEST_F(ValidateImage, SampleProjImplicitLodNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, SampleProjImplicitLodMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec2_hh Sample %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'MS' parameter to be 0")); +} + TEST_F(ValidateImage, SampleProjImplicitLodWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -1848,6 +2186,21 @@ TEST_F(ValidateImage, SampleDrefImplicitLodNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, SampleDrefImplicitLodMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %f32 %simg %f32vec2_hh %f32_1 Sample %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Dref sampling operation is invalid for multisample image")); +} + TEST_F(ValidateImage, SampleDrefImplicitLodWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 @@ -1971,6 +2324,21 @@ TEST_F(ValidateImage, SampleDrefExplicitLodNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, SampleDrefExplicitLodMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %f32 %simg %f32vec2_hh %f32_1 Lod|Sample %f32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Dref sampling operation is invalid for multisample image")); +} + TEST_F(ValidateImage, SampleDrefExplicitLodWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 @@ -2095,6 +2463,21 @@ TEST_F(ValidateImage, SampleProjDrefImplicitLodNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, SampleProjDrefImplicitLodMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %f32 %simg %f32vec2_hh %f32_1 Sample %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Dref sampling operation is invalid for multisample image")); +} + TEST_F(ValidateImage, SampleProjDrefImplicitLodWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -2218,6 +2601,21 @@ TEST_F(ValidateImage, SampleProjDrefExplicitLodNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, SampleProjDrefExplicitLodMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %f32 %simg %f32vec2_hh %f32_1 Lod|Sample %f32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Dref sampling operation is invalid for multisample image")); +} + TEST_F(ValidateImage, SampleProjDrefExplicitLodWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 @@ -2294,6 +2692,23 @@ OpExtension "SPV_KHR_vulkan_memory_model" ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } +TEST_F(ValidateImage, FetchMultisampledSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 Sample %u32_1 +%res2 = OpImageFetch %f32vec4 %img %u32vec2_01 Sample|NonPrivateTexelKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + TEST_F(ValidateImage, FetchWrongResultType) { const std::string body = R"( %img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 @@ -2349,7 +2764,7 @@ TEST_F(ValidateImage, FetchSampledImageDirectly) { TEST_F(ValidateImage, FetchNotSampled) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageFetch %u32vec4 %img %u32vec2_01 )"; @@ -2433,6 +2848,21 @@ TEST_F(ValidateImage, FetchLodNotInt) { "with OpImageFetch")); } +TEST_F(ValidateImage, FetchMultisampledMissingSample) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()) + << GenerateShaderCode(body); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Sample is required for operation on " + "multi-sampled image")) + << getDiagnosticString(); +} + TEST_F(ValidateImage, GatherSuccess) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -2494,6 +2924,20 @@ TEST_F(ValidateImage, GatherNotSampledImage) { HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); } +TEST_F(ValidateImage, GatherMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 Sample %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Gather operation is invalid for multisample image")); +} + TEST_F(ValidateImage, GatherWrongSampledType) { const std::string body = R"( %img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 @@ -2578,6 +3022,40 @@ TEST_F(ValidateImage, GatherComponentNot32Bit) { HasSubstr("Expected Component to be 32-bit int scalar")); } +TEST_F(ValidateImage, GatherComponentSuccessVulkan) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(), + env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateImage, GatherComponentNotConstantVulkan) { + const std::string body = R"( +%input_u32 = OpLoad %u32 %input_flat_u32 +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %input_u32 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(), + env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImageGather-04664")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Component Operand to be a const object for " + "Vulkan environment")); +} + TEST_F(ValidateImage, GatherDimCube) { const std::string body = R"( %img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 @@ -2709,6 +3187,20 @@ OpExtension "SPV_KHR_vulkan_memory_model" ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } +TEST_F(ValidateImage, DrefGatherMultisampleError) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler +%res1 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %f32_1 Sample %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Gather operation is invalid for multisample image")); +} + TEST_F(ValidateImage, DrefGatherVoidSampledType) { const std::string body = R"( %img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 @@ -2740,7 +3232,7 @@ TEST_F(ValidateImage, DrefGatherWrongDrefType) { TEST_F(ValidateImage, ReadSuccess1) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 )"; @@ -2783,7 +3275,7 @@ TEST_F(ValidateImage, ReadSuccess4) { TEST_F(ValidateImage, ReadNeedCapabilityStorageImageReadWithoutFormat) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 )"; @@ -2793,7 +3285,7 @@ TEST_F(ValidateImage, ReadNeedCapabilityStorageImageReadWithoutFormat) { TEST_F(ValidateImage, ReadNeedCapabilityStorageImageReadWithoutFormatVulkan) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 )"; @@ -2836,7 +3328,7 @@ TEST_F(ValidateImage, ReadNeedCapabilityImageCubeArray) { // TODO(atgoo@github.com) Disabled until the spec is clarified. TEST_F(ValidateImage, DISABLED_ReadWrongResultType) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %f32 %img %u32vec2_01 )"; @@ -2847,16 +3339,41 @@ TEST_F(ValidateImage, DISABLED_ReadWrongResultType) { HasSubstr("Expected Result Type to be int or float vector type")); } -// TODO(atgoo@github.com) Disabled until the spec is clarified. -TEST_F(ValidateImage, DISABLED_ReadWrongNumComponentsResultType) { +TEST_F(ValidateImage, ReadScalarResultType_Universal) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 -%res1 = OpImageRead %f32vec3 %img %u32vec2_01 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 +%res1 = OpImageRead %u32 %img %u32vec2_01 )"; const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, ReadUnusualNumComponentsResultType_Universal) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 +%res1 = OpImageRead %u32vec3 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, ReadWrongNumComponentsResultType_Vulkan) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 +%res1 = OpImageRead %u32vec3 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_VULKAN_1_0) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Result Type to have 4 components")); } @@ -2889,7 +3406,7 @@ TEST_F(ValidateImage, ReadImageSampled) { TEST_F(ValidateImage, ReadWrongSampledType) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %f32vec4 %img %u32vec2_01 )"; @@ -2916,7 +3433,7 @@ TEST_F(ValidateImage, ReadVoidSampledType) { TEST_F(ValidateImage, ReadWrongCoordinateType) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %f32vec2_00 )"; @@ -2929,7 +3446,7 @@ TEST_F(ValidateImage, ReadWrongCoordinateType) { TEST_F(ValidateImage, ReadCoordinateSizeTooSmall) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32_1 )"; @@ -2943,7 +3460,7 @@ TEST_F(ValidateImage, ReadCoordinateSizeTooSmall) { TEST_F(ValidateImage, WriteSuccess1) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec4_0123 )"; @@ -2976,14 +3493,24 @@ OpImageWrite %img %u32vec3_012 %f32vec4_0000 TEST_F(ValidateImage, WriteSuccess4) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 -;TODO(atgoo@github.com) Is it legal to write to MS image without sample index? -OpImageWrite %img %u32vec2_01 %f32vec4_0000 +%img = OpLoad %type_image_f32_2d_0012 %uniform_image_f32_2d_0012 OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1 )"; - const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; - CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + const std::string extra = R"( + OpCapability StorageImageWriteWithoutFormat + OpCapability StorageImageMultisample + )"; + + const std::string declarations = R"( +%type_image_f32_2d_0012 = OpTypeImage %f32 2D 0 0 1 2 Unknown +%ptr_image_f32_2d_0012 = OpTypePointer UniformConstant %type_image_f32_2d_0012 +%uniform_image_f32_2d_0012 = OpVariable %ptr_image_f32_2d_0012 UniformConstant + )"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_0, "GLSL450", + declarations) + .c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -3001,7 +3528,7 @@ OpImageWrite %img %u32vec2_01 %f32vec4_0000 TEST_F(ValidateImage, WriteNeedCapabilityStorageImageWriteWithoutFormat) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec4_0123 )"; @@ -3011,7 +3538,7 @@ OpImageWrite %img %u32vec2_01 %u32vec4_0123 TEST_F(ValidateImage, WriteNeedCapabilityStorageImageWriteWithoutFormatVulkan) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec4_0123 )"; @@ -3080,7 +3607,7 @@ OpImageWrite %img %u32vec2_01 %f32vec4_0000 TEST_F(ValidateImage, WriteWrongCoordinateType) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %f32vec2_00 %u32vec4_0123 )"; @@ -3093,7 +3620,7 @@ OpImageWrite %img %f32vec2_00 %u32vec4_0123 TEST_F(ValidateImage, WriteCoordinateSizeTooSmall) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32_1 %u32vec4_0123 )"; @@ -3107,7 +3634,7 @@ OpImageWrite %img %u32_1 %u32vec4_0123 TEST_F(ValidateImage, WriteTexelWrongType) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %img )"; @@ -3120,7 +3647,7 @@ OpImageWrite %img %u32vec2_01 %img TEST_F(ValidateImage, DISABLED_WriteTexelNotVector4) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec3_012 )"; @@ -3133,7 +3660,7 @@ OpImageWrite %img %u32vec2_01 %u32vec3_012 TEST_F(ValidateImage, WriteTexelWrongComponentType) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %f32vec4_0000 )"; @@ -3148,18 +3675,29 @@ OpImageWrite %img %u32vec2_01 %f32vec4_0000 TEST_F(ValidateImage, WriteSampleNotInteger) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%img = OpLoad %type_image_f32_2d_0012 %uniform_image_f32_2d_0012 OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %f32_1 )"; - const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; - CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + const std::string extra = R"( + OpCapability StorageImageWriteWithoutFormat + OpCapability StorageImageMultisample + )"; + const std::string declarations = R"( +%type_image_f32_2d_0012 = OpTypeImage %f32 2D 0 0 1 2 Unknown +%ptr_image_f32_2d_0012 = OpTypePointer UniformConstant %type_image_f32_2d_0012 +%uniform_image_f32_2d_0012 = OpVariable %ptr_image_f32_2d_0012 UniformConstant + )"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_0, "GLSL450", + declarations) + .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Image Operand Sample to be int scalar")); } -TEST_F(ValidateImage, SampleNotMultisampled) { +TEST_F(ValidateImage, WriteSampleNotMultisampled) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1 @@ -3175,18 +3713,16 @@ OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1 TEST_F(ValidateImage, SampleWrongOpcode) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 %sampler = OpLoad %type_sampler %uniform_sampler -%simg = OpSampledImage %type_sampled_image_f32_2d_0010 %img %sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler %res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Sample %u32_1 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image Operand Sample can only be used with " - "OpImageFetch, OpImageRead, OpImageWrite, " - "OpImageSparseFetch and OpImageSparseRead")); + HasSubstr("Sampling operation is invalid for multisample image")); } TEST_F(ValidateImage, SampleImageToImageSuccess) { @@ -3379,27 +3915,79 @@ TEST_F(ValidateImage, QuerySizeLodSampledImageDirectly) { "for OpImageQuerySizeLod")); } -TEST_F(ValidateImage, QuerySizeLodWrongImageDim) { +TEST_F(ValidateImage, QuerySizeLodMultisampledError) { const std::string body = R"( -%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 %res1 = OpImageQuerySizeLod %u32vec2 %img %u32_1 )"; CompileSuccessfully(GenerateKernelCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Image 'MS' must be 0")); +} + +TEST_F(ValidateImage, QuerySizeLodNonSampledUniversalSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageQuerySizeLod %u32vec2 %img %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateImage, QuerySizeLodVulkanNonSampledError) { + // Create a whole shader module. Avoid Vulkan incompatibility with + // SampledRrect images inserted by helper function GenerateShaderCode. + const std::string body = R"( +OpCapability Shader +OpCapability ImageQuery +OpMemoryModel Logical Simple +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%u32_0 = OpConstant %u32 0 +%u32vec2 = OpTypeVector %u32 2 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void + +; Test with a storage image. +%type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Rgba32f +%ptr_image_f32_2d_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002 +%uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant + +%main = OpFunction %void None %voidfn +%entry = OpLabel +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageQuerySizeLod %u32vec2 %img %u32_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Image 'Dim' must be 1D, 2D, 3D or Cube")); + AnyVUID("VUID-StandaloneSpirv-OpImageQuerySizeLod-04659")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpImageQuerySizeLod must only consume an \"Image\" operand whose " + "type has its \"Sampled\" operand set to 1")); } -TEST_F(ValidateImage, QuerySizeLodMultisampled) { +TEST_F(ValidateImage, QuerySizeLodWrongImageDim) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 %res1 = OpImageQuerySizeLod %u32vec2 %img %u32_1 )"; CompileSuccessfully(GenerateKernelCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Image 'MS' must be 0")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image 'Dim' must be 1D, 2D, 3D or Cube")); } TEST_F(ValidateImage, QuerySizeLodWrongLodType) { @@ -3416,7 +4004,7 @@ TEST_F(ValidateImage, QuerySizeLodWrongLodType) { TEST_F(ValidateImage, QuerySizeSuccess) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 %res1 = OpImageQuerySize %u32vec2 %img )"; @@ -3426,7 +4014,7 @@ TEST_F(ValidateImage, QuerySizeSuccess) { TEST_F(ValidateImage, QuerySizeWrongResultType) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 %res1 = OpImageQuerySize %f32vec2 %img )"; @@ -3439,7 +4027,7 @@ TEST_F(ValidateImage, QuerySizeWrongResultType) { TEST_F(ValidateImage, QuerySizeNotImage) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler %res1 = OpImageQuerySize %u32vec2 %sampler @@ -3453,7 +4041,7 @@ TEST_F(ValidateImage, QuerySizeNotImage) { TEST_F(ValidateImage, QuerySizeSampledImageDirectly) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 %sampler = OpLoad %type_sampler %uniform_sampler %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler %res1 = OpImageQuerySize %u32vec2 %simg @@ -3676,9 +4264,58 @@ TEST_F(ValidateImage, QueryLevelsWrongDim) { HasSubstr("Image 'Dim' must be 1D, 2D, 3D or Cube")); } +TEST_F(ValidateImage, QuerySizeLevelsNonSampledUniversalSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageQueryLevels %u32 %img +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateImage, QuerySizeLevelsVulkanNonSampledError) { + // Create a whole shader module. Avoid Vulkan incompatibility with + // SampledRrect images inserted by helper function GenerateShaderCode. + const std::string body = R"( +OpCapability Shader +OpCapability ImageQuery +OpMemoryModel Logical Simple +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void + +; Test with a storage image. +%type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Rgba32f +%ptr_image_f32_2d_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002 +%uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant + +%main = OpFunction %void None %voidfn +%entry = OpLabel +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageQueryLevels %u32 %img +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImageQuerySizeLod-04659")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpImageQueryLevels must only consume an \"Image\" operand " + "whose type has its \"Sampled\" operand set to 1")); +} + TEST_F(ValidateImage, QuerySamplesSuccess) { const std::string body = R"( -%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011 %res1 = OpImageQuerySamples %u32 %img )"; @@ -3688,7 +4325,7 @@ TEST_F(ValidateImage, QuerySamplesSuccess) { TEST_F(ValidateImage, QuerySamplesNot2D) { const std::string body = R"( -%img = OpLoad %type_image_f32_3d_0010 %uniform_image_f32_3d_0010 +%img = OpLoad %type_image_f32_3d_0011 %uniform_image_f32_3d_0011 %res1 = OpImageQuerySamples %u32 %img )"; @@ -3766,6 +4403,102 @@ OpExecutionMode %main DerivativeGroupLinearNV ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateImage, QueryLodUniversalSuccess) { + // Create a whole shader module. Avoid Vulkan incompatibility with + // SampledRrect images inserted by helper function GenerateShaderCode. + const std::string body = R"( +OpCapability Shader +OpCapability ImageQuery +OpMemoryModel Logical Simple +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +OpDecorate %uniform_image_f32_2d_0000 DescriptorSet 0 +OpDecorate %uniform_image_f32_2d_0000 Binding 0 +OpDecorate %sampler DescriptorSet 0 +OpDecorate %sampler Binding 1 + +%f32 = OpTypeFloat 32 +%f32vec2 = OpTypeVector %f32 2 +%f32vec2_null = OpConstantNull %f32vec2 +%u32 = OpTypeInt 32 0 +%u32vec2 = OpTypeVector %u32 2 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void + +; Test with an image with sampled = 0 +%type_image_f32_2d_0000 = OpTypeImage %f32 2D 0 0 0 0 Rgba32f +%ptr_image_f32_2d_0000 = OpTypePointer UniformConstant %type_image_f32_2d_0000 +%uniform_image_f32_2d_0000 = OpVariable %ptr_image_f32_2d_0000 UniformConstant +%sampled_image_ty = OpTypeSampledImage %type_image_f32_2d_0000 + +%sampler_ty = OpTypeSampler +%ptr_sampler_ty = OpTypePointer UniformConstant %sampler_ty +%sampler = OpVariable %ptr_sampler_ty UniformConstant + + +%main = OpFunction %void None %voidfn +%entry = OpLabel +%img = OpLoad %type_image_f32_2d_0000 %uniform_image_f32_2d_0000 +%s = OpLoad %sampler_ty %sampler +%simg = OpSampledImage %sampled_image_ty %img %s +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_null +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QueryLodVulkanNonSampledError) { + // Create a whole shader module. Avoid Vulkan incompatibility with + // SampledRrect images inserted by helper function GenerateShaderCode. + const std::string body = R"( +OpCapability Shader +OpCapability ImageQuery +OpMemoryModel Logical Simple +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +OpDecorate %sampled_image DescriptorSet 0 +OpDecorate %sampled_image Binding 0 + +%f32 = OpTypeFloat 32 +%f32vec2 = OpTypeVector %f32 2 +%f32vec2_null = OpConstantNull %f32vec2 +%u32 = OpTypeInt 32 0 +%u32vec2 = OpTypeVector %u32 2 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void + +; Test with an image with Sampled = 2 +; In Vulkan it Sampled must be 1 or 2, checked in another part of the +; validation flow. +%type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Rgba32f + +; Expect to fail here. +%sampled_image_ty = OpTypeSampledImage %type_image_f32_2d_0002 +%ptr_sampled_image_ty = OpTypePointer UniformConstant %sampled_image_ty +%sampled_image = OpVariable %ptr_sampled_image_ty UniformConstant + +%main = OpFunction %void None %voidfn +%entry = OpLabel +%simg = OpLoad %sampled_image_ty %sampled_image +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_null +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-04657")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled image type requires an image type with " + "\"Sampled\" operand set to 0 or 1")); +} + TEST_F(ValidateImage, QueryLodComputeShaderDerivativesMissingMode) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -4453,7 +5186,7 @@ TEST_F(ValidateImage, SparseTexelsResidentResultTypeNotBool) { TEST_F(ValidateImage, MakeTexelVisibleKHRSuccessImageRead) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 MakeTexelVisibleKHR|NonPrivateTexelKHR %u32_2 )"; @@ -4511,7 +5244,7 @@ OpExtension "SPV_KHR_vulkan_memory_model" TEST_F(ValidateImage, MakeTexelVisibleKHRFailureMissingNonPrivate) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 MakeTexelVisibleKHR %u32_1 )"; @@ -4532,7 +5265,7 @@ OpExtension "SPV_KHR_vulkan_memory_model" TEST_F(ValidateImage, MakeTexelAvailableKHRSuccessImageWrite) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_2 )"; @@ -4572,7 +5305,7 @@ OpExtension "SPV_KHR_vulkan_memory_model" TEST_F(ValidateImage, MakeTexelAvailableKHRFailureMissingNonPrivate) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR %u32_1 )"; @@ -4593,7 +5326,7 @@ OpExtension "SPV_KHR_vulkan_memory_model" TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageWriteBad) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1 )"; @@ -4615,7 +5348,7 @@ OpExtension "SPV_KHR_vulkan_memory_model" TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageWriteGood) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1 )"; @@ -4633,7 +5366,7 @@ OpExtension "SPV_KHR_vulkan_memory_model" TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageReadBad) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 MakeTexelVisibleKHR|NonPrivateTexelKHR %u32_1 )"; @@ -4655,7 +5388,7 @@ OpExtension "SPV_KHR_vulkan_memory_model" TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageReadGood) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 MakeTexelVisibleKHR|NonPrivateTexelKHR %u32_1 )"; @@ -4708,7 +5441,7 @@ TEST_F(ValidateImage, Issue2463NoSegFault) { TEST_F(ValidateImage, SignExtendV13Bad) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend )"; @@ -4719,7 +5452,7 @@ TEST_F(ValidateImage, SignExtendV13Bad) { TEST_F(ValidateImage, ZeroExtendV13Bad) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend )"; @@ -4731,7 +5464,7 @@ TEST_F(ValidateImage, ZeroExtendV13Bad) { TEST_F(ValidateImage, SignExtendScalarUIntTexelV14Good) { // Unsigned int sampled type const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32 %img %u32vec2_01 SignExtend )"; const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; @@ -4760,7 +5493,7 @@ TEST_F(ValidateImage, SignExtendScalarSIntTexelV14Good) { TEST_F(ValidateImage, SignExtendScalarVectorUIntTexelV14Good) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend )"; const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; @@ -4792,7 +5525,7 @@ TEST_F(ValidateImage, SignExtendVectorSIntTexelV14Good) { TEST_F(ValidateImage, ZeroExtendScalarUIntTexelV14Good) { // Unsigned int sampled type const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32 %img %u32vec2_01 ZeroExtend )"; const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; @@ -4821,7 +5554,7 @@ TEST_F(ValidateImage, ZeroExtendScalarSIntTexelV14Good) { TEST_F(ValidateImage, ZeroExtendScalarVectorUIntTexelV14Good) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend )"; const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; @@ -4849,7 +5582,7 @@ TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14Good) { TEST_F(ValidateImage, ReadLodAMDSuccess1) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 %res1 = OpImageRead %u32vec4 %img %u32vec2_01 Lod %u32_0 )"; @@ -4914,7 +5647,7 @@ TEST_F(ValidateImage, ReadLodAMDNeedCapability) { TEST_F(ValidateImage, WriteLodAMDSuccess1) { const std::string body = R"( -%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 OpImageWrite %img %u32vec2_01 %u32vec4_0123 Lod %u32_0 )"; @@ -5088,6 +5821,12 @@ static const std::string declarations_image64 = R"( %ptr_image_u64_buffer_0002_r64ui = OpTypePointer Private %type_image_u64_buffer_0002_r64ui %private_image_u64_buffer_0002_r64ui = OpVariable %ptr_image_u64_buffer_0002_r64ui Private )"; +static const std::string declarations_image64i = R"( +%type_image_s64_buffer_0002_r64i = OpTypeImage %s64 Buffer 0 0 0 2 R64i +%ptr_Image_s64 = OpTypePointer Image %s64 +%ptr_image_s64_buffer_0002_r64i = OpTypePointer Private %type_image_s64_buffer_0002_r64i +%private_image_s64_buffer_0002_r64i = OpVariable %ptr_image_s64_buffer_0002_r64i Private +)"; TEST_F(ValidateImage, Image64MissingCapability) { CompileSuccessfully(GenerateShaderCode("", "", "Fragment", "", @@ -5173,6 +5912,134 @@ TEST_F(ValidateImage, ImageTexelPointer64SampleNotZeroForImageWithMSZero) { "<id> for the value 0")); } +TEST_F(ValidateImage, ImageTexelPointerR32uiSuccessVulkan) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(), + env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateImage, ImageTexelPointerR32iSuccessVulkan) { + const std::string& declarations = R"( +%type_image_s32_buffer_0002_r32i = OpTypeImage %s32 Buffer 0 0 0 2 R32i +%ptr_Image_s32 = OpTypePointer Image %s32 +%ptr_image_s32_buffer_0002_r32i = OpTypePointer Private %type_image_s32_buffer_0002_r32i +%private_image_s32_buffer_0002_r32i = OpVariable %ptr_image_s32_buffer_0002_r32i Private +)"; + + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_s32 %private_image_s32_buffer_0002_r32i %u32_0 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", env, "GLSL450", declarations) + .c_str(), + env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateImage, ImageTexelPointerR64uiSuccessVulkan) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u64 %private_image_u64_buffer_0002_r64ui %u32_0 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully( + GenerateShaderCode(body, capabilities_and_extensions_image64, "Fragment", + "", env, "GLSL450", declarations_image64) + .c_str(), + env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateImage, ImageTexelPointerR64iSuccessVulkan) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_s64 %private_image_s64_buffer_0002_r64i %u32_0 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully( + GenerateShaderCode(body, capabilities_and_extensions_image64, "Fragment", + "", env, "GLSL450", declarations_image64i) + .c_str(), + env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateImage, ImageTexelPointerR32fSuccessVulkan) { + const std::string& declarations = R"( +%type_image_f32_buffer_0002_r32f = OpTypeImage %f32 Buffer 0 0 0 2 R32f +%ptr_image_f32_buffer_0002_r32f = OpTypePointer Private %type_image_f32_buffer_0002_r32f +%private_image_f32_buffer_0002_r32f = OpVariable %ptr_image_f32_buffer_0002_r32f Private +)"; + + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_r32f %u32_0 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", env, "GLSL450", declarations) + .c_str(), + env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateImage, ImageTexelPointerRgba32iVulkan) { + const std::string& declarations = R"( +%type_image_s32_buffer_0002_rgba32i = OpTypeImage %s32 Buffer 0 0 0 2 Rgba32i +%ptr_Image_s32 = OpTypePointer Image %s32 +%ptr_image_s32_buffer_0002_rgba32i = OpTypePointer Private %type_image_s32_buffer_0002_rgba32i +%private_image_s32_buffer_0002_rgba32i = OpVariable %ptr_image_s32_buffer_0002_rgba32i Private +)"; + + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_s32 %private_image_s32_buffer_0002_rgba32i %u32_0 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", env, "GLSL450", declarations) + .c_str(), + env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImageTexelPointer-04658")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected the Image Format in Image to be R64i, R64ui, " + "R32f, R32i, or R32ui for Vulkan environment")); +} + +TEST_F(ValidateImage, ImageTexelPointerRgba16fVulkan) { + const std::string& declarations = R"( +%type_image_s32_buffer_0002_rgba16f = OpTypeImage %s32 Buffer 0 0 0 2 Rgba16f +%ptr_Image_s32 = OpTypePointer Image %s32 +%ptr_image_s32_buffer_0002_rgba16f = OpTypePointer Private %type_image_s32_buffer_0002_rgba16f +%private_image_s32_buffer_0002_rgba16f = OpVariable %ptr_image_s32_buffer_0002_rgba16f Private +)"; + + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_s32 %private_image_s32_buffer_0002_rgba16f %u32_0 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", env, "GLSL450", declarations) + .c_str(), + env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImageTexelPointer-04658")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected the Image Format in Image to be R64i, R64ui, " + "R32f, R32i, or R32ui for Vulkan environment")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp index 43fa0469..d34c97f9 100644 --- a/test/val/val_layout_test.cpp +++ b/test/val/val_layout_test.cpp @@ -667,57 +667,6 @@ TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) { HasSubstr("ModuleProcessed cannot appear in a function declaration")); } -TEST_F(ValidateLayout, WebGPUCallerBeforeCalleeBad) { - char str[] = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint GLCompute %main "main" -%void = OpTypeVoid -%voidfn = OpTypeFunction %void -%main = OpFunction %void None %voidfn -%1 = OpLabel -%2 = OpFunctionCall %void %callee - OpReturn - OpFunctionEnd -%callee = OpFunction %void None %voidfn -%3 = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(str, SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, functions need to be defined before being " - "called.\n %5 = OpFunctionCall %void %6\n")); -} - -TEST_F(ValidateLayout, WebGPUCalleeBeforeCallerGood) { - char str[] = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint GLCompute %main "main" -%void = OpTypeVoid -%voidfn = OpTypeFunction %void -%callee = OpFunction %void None %voidfn -%3 = OpLabel - OpReturn - OpFunctionEnd -%main = OpFunction %void None %voidfn -%1 = OpLabel -%2 = OpFunctionCall %void %callee - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(str, SPV_ENV_WEBGPU_0); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - // TODO(umar): Test optional instructions } // namespace diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index e5418905..9799b404 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -22,6 +22,16 @@ #include "test/val/val_code_generator.h" #include "test/val/val_fixtures.h" +// For pretty-printing tuples with spv_target_env. +std::ostream& operator<<(std::ostream& stream, spv_target_env target) +{ + switch (target) { + case SPV_ENV_UNIVERSAL_1_3: return stream << "SPV_ENV_UNIVERSAL_1_3"; + case SPV_ENV_UNIVERSAL_1_4: return stream << "SPV_ENV_UNIVERSAL_1_4"; + default: return stream << (unsigned)target; + } +} + namespace spvtools { namespace val { namespace { @@ -51,14 +61,14 @@ OpFunctionEnd )"; CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-04655")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\n" - "Variables identified with the UniformConstant storage class " + HasSubstr("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, OpTypeAccelerationStructureNV, " - "OpTypeAccelerationStructureKHR, OpTypeRayQueryKHR, " + "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " "or an array of one of these types.")); } @@ -105,14 +115,14 @@ OpFunctionEnd )"; CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-04655")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\n" - "Variables identified with the UniformConstant storage class " + HasSubstr("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, OpTypeAccelerationStructureNV, " - "OpTypeAccelerationStructureKHR, OpTypeRayQueryKHR, " + "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " "or an array of one of these types.")); } @@ -362,192 +372,6 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateMemory, WebGPUInitializerWithOutputStorageClassesGood) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%float = OpTypeFloat 32 -%float_ptr = OpTypePointer Output %float -%init_val = OpConstant %float 1.0 -%1 = OpVariable %float_ptr Output %init_val -%void = OpTypeVoid -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%2 = OpLabel -OpReturn -OpFunctionEnd -)"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateMemory, WebGPUInitializerWithFunctionStorageClassesGood) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%float = OpTypeFloat 32 -%float_ptr = OpTypePointer Function %float -%init_val = OpConstant %float 1.0 -%void = OpTypeVoid -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -%2 = OpVariable %float_ptr Function %init_val -OpReturn -OpFunctionEnd -)"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateMemory, WebGPUInitializerWithPrivateStorageClassesGood) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%float = OpTypeFloat 32 -%float_ptr = OpTypePointer Private %float -%init_val = OpConstant %float 1.0 -%1 = OpVariable %float_ptr Private %init_val -%void = OpTypeVoid -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%2 = OpLabel -OpReturn -OpFunctionEnd -)"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateMemory, WebGPUInitializerWithDisallowedStorageClassesBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%float = OpTypeFloat 32 -%float_ptr = OpTypePointer Uniform %float -%init_val = OpConstant %float 1.0 -%1 = OpVariable %float_ptr Uniform %init_val -%void = OpTypeVoid -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%2 = OpLabel -OpReturn -OpFunctionEnd -)"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpVariable, <id> '5[%5]', has a disallowed initializer & " - "storage class combination.\nFrom WebGPU spec:\nVariable " - "declarations that include initializers must have one of the " - "following storage classes: Output, Private, or Function\n %5 " - "= OpVariable %_ptr_Uniform_float Uniform %float_1\n")); -} - -TEST_F(ValidateMemory, WebGPUOutputStorageClassWithoutInitializerBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%float = OpTypeFloat 32 -%float_ptr = OpTypePointer Output %float -%1 = OpVariable %float_ptr Output -%void = OpTypeVoid -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%2 = OpLabel -OpReturn -OpFunctionEnd -)"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpVariable, <id> '4[%4]', must have an initializer.\n" - "From WebGPU execution environment spec:\n" - "All variables in the following storage classes must have an " - "initializer: Output, Private, or Function\n" - " %4 = OpVariable %_ptr_Output_float Output\n")); -} - -TEST_F(ValidateMemory, WebGPUFunctionStorageClassWithoutInitializerBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%float = OpTypeFloat 32 -%float_ptr = OpTypePointer Function %float -%void = OpTypeVoid -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -%2 = OpVariable %float_ptr Function -OpReturn -OpFunctionEnd -)"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpVariable, <id> '7[%7]', must have an initializer.\n" - "From WebGPU execution environment spec:\n" - "All variables in the following storage classes must have an " - "initializer: Output, Private, or Function\n" - " %7 = OpVariable %_ptr_Function_float Function\n")); -} - -TEST_F(ValidateMemory, WebGPUPrivateStorageClassWithoutInitializerBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%float = OpTypeFloat 32 -%float_ptr = OpTypePointer Private %float -%1 = OpVariable %float_ptr Private -%void = OpTypeVoid -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%2 = OpLabel -OpReturn -OpFunctionEnd -)"; - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpVariable, <id> '4[%4]', must have an initializer.\n" - "From WebGPU execution environment spec:\n" - "All variables in the following storage classes must have an " - "initializer: Output, Private, or Function\n" - " %4 = OpVariable %_ptr_Private_float Private\n")); -} - TEST_F(ValidateMemory, VulkanInitializerWithOutputStorageClassesGood) { std::string spirv = R"( OpCapability Shader @@ -630,12 +454,15 @@ OpFunctionEnd )"; CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpVariable-04651")); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpVariable, <id> '5[%5]', has a disallowed initializer & " "storage class combination.\nFrom Vulkan spec:\nVariable " "declarations that include initializers must have one of the " - "following storage classes: Output, Private, or Function\n %5 " + "following storage classes: Output, Private, Function or " + "Workgroup\n %5 " "= OpVariable %_ptr_Input_float Input %float_1\n")); } @@ -2280,38 +2107,6 @@ OpFunctionEnd "%_ptr_UniformConstant__runtimearr_2 UniformConstant\n")); } -TEST_F(ValidateMemory, WebGPURTAOutsideOfStructBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%sampler_t = OpTypeSampler -%array_t = OpTypeRuntimeArray %sampler_t -%array_ptr = OpTypePointer UniformConstant %array_t -%2 = OpVariable %array_ptr UniformConstant -%void = OpTypeVoid -%func_t = OpTypeFunction %void -%func = OpFunction %void None %func_t -%1 = OpLabel -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "OpVariable, <id> '5[%5]', is attempting to create memory for an " - "illegal type, OpTypeRuntimeArray.\nFor WebGPU OpTypeRuntimeArray " - "can only appear as the final member of an OpTypeStruct, thus cannot " - "be instantiated via OpVariable\n %5 = OpVariable " - "%_ptr_UniformConstant__runtimearr_2 UniformConstant\n")); -} - TEST_F(ValidateMemory, VulkanRTAOutsideOfStructWithRuntimeDescriptorArrayGood) { std::string spirv = R"( OpCapability Shader @@ -2401,34 +2196,6 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } -TEST_F(ValidateMemory, WebGPURTAInsideStorageBufferStructGood) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t ArrayStride 4 -OpMemberDecorate %struct_t 0 Offset 0 -OpDecorate %struct_t Block -%uint_t = OpTypeInt 32 0 -%array_t = OpTypeRuntimeArray %uint_t -%struct_t = OpTypeStruct %array_t -%struct_ptr = OpTypePointer StorageBuffer %struct_t -%2 = OpVariable %struct_ptr StorageBuffer -%void = OpTypeVoid -%func_t = OpTypeFunction %void -%func = OpFunction %void None %func_t -%1 = OpLabel -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - TEST_F(ValidateMemory, VulkanRTAInsideWrongStorageClassStructBad) { std::string spirv = R"( OpCapability Shader @@ -2458,36 +2225,6 @@ OpFunctionEnd "OpVariable %_ptr_Workgroup__struct_4 Workgroup\n")); } -TEST_F(ValidateMemory, WebGPURTAInsideWrongStorageClassStructBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%uint_t = OpTypeInt 32 0 -%array_t = OpTypeRuntimeArray %uint_t -%struct_t = OpTypeStruct %array_t -%struct_ptr = OpTypePointer Workgroup %struct_t -%2 = OpVariable %struct_ptr Workgroup -%void = OpTypeVoid -%func_t = OpTypeFunction %void -%func = OpFunction %void None %func_t -%1 = OpLabel -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("For WebGPU, OpTypeStruct variables containing " - "OpTypeRuntimeArray must have storage class of StorageBuffer\n " - " %6 = OpVariable %_ptr_Workgroup__struct_4 Workgroup\n")); -} - TEST_F(ValidateMemory, VulkanRTAInsideStorageBufferStructWithoutBlockBad) { std::string spirv = R"( OpCapability Shader @@ -2516,36 +2253,6 @@ OpFunctionEnd "%_ptr_StorageBuffer__struct_4 StorageBuffer\n")); } -TEST_F(ValidateMemory, WebGPURTAInsideStorageBufferStructWithoutBlockBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%uint_t = OpTypeInt 32 0 -%array_t = OpTypeRuntimeArray %uint_t -%struct_t = OpTypeStruct %array_t -%struct_ptr = OpTypePointer StorageBuffer %struct_t -%2 = OpVariable %struct_ptr StorageBuffer -%void = OpTypeVoid -%func_t = OpTypeFunction %void -%func = OpFunction %void None %func_t -%1 = OpLabel -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, an OpTypeStruct variable containing an " - "OpTypeRuntimeArray must be decorated with Block if it " - "has storage class StorageBuffer.\n %6 = OpVariable " - "%_ptr_StorageBuffer__struct_4 StorageBuffer\n")); -} - TEST_F(ValidateMemory, VulkanRTAInsideUniformStructGood) { std::string spirv = R"( OpCapability Shader @@ -2572,39 +2279,6 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } -TEST_F(ValidateMemory, WebGPURTAInsideUniformStructBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -OpDecorate %array_t ArrayStride 4 -OpMemberDecorate %struct_t 0 Offset 0 -OpDecorate %struct_t Block -%uint_t = OpTypeInt 32 0 -%array_t = OpTypeRuntimeArray %uint_t -%struct_t = OpTypeStruct %array_t -%struct_ptr = OpTypePointer Uniform %struct_t -%2 = OpVariable %struct_ptr Uniform -%void = OpTypeVoid -%func_t = OpTypeFunction %void -%func = OpFunction %void None %func_t -%1 = OpLabel -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("For WebGPU, OpTypeStruct variables containing " - "OpTypeRuntimeArray must have storage class of StorageBuffer\n " - " %6 = OpVariable %_ptr_Uniform__struct_3 Uniform\n")); -} - TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlockBad) { std::string spirv = R"( OpCapability Shader @@ -2662,37 +2336,6 @@ OpFunctionEnd "OpTypeRuntimeArray %_runtimearr_2\n")); } -TEST_F(ValidateMemory, WebGPURTAInsideRTABad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%sampler_t = OpTypeSampler -%inner_array_t = OpTypeRuntimeArray %sampler_t -%array_t = OpTypeRuntimeArray %inner_array_t -%array_ptr = OpTypePointer UniformConstant %array_t -%2 = OpVariable %array_ptr UniformConstant -%void = OpTypeVoid -%func_t = OpTypeFunction %void -%func = OpFunction %void None %func_t -%1 = OpLabel -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr( - "OpTypeRuntimeArray Element Type <id> '3[%_runtimearr_2]' is not " - "valid in WebGPU environments.\n %_runtimearr__runtimearr_2 = " - "OpTypeRuntimeArray %_runtimearr_2\n")); -} - TEST_F(ValidateMemory, VulkanRTAInsideRTAWithRuntimeDescriptorArrayBad) { std::string spirv = R"( OpCapability RuntimeDescriptorArrayEXT @@ -2853,38 +2496,6 @@ OpFunctionEnd "OpTypeArray %_runtimearr_4 %uint_1\n")); } -TEST_F(ValidateMemory, WebGPURTAInsideArrayBad) { - std::string spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%uint_t = OpTypeInt 32 0 -%dim = OpConstant %uint_t 1 -%sampler_t = OpTypeSampler -%inner_array_t = OpTypeRuntimeArray %sampler_t -%array_t = OpTypeArray %inner_array_t %dim -%array_ptr = OpTypePointer UniformConstant %array_t -%2 = OpVariable %array_ptr UniformConstant -%void = OpTypeVoid -%func_t = OpTypeFunction %void -%func = OpFunction %void None %func_t -%1 = OpLabel -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not " - "valid in WebGPU environments.\n %_arr__runtimearr_4_uint_1 = " - "OpTypeArray %_runtimearr_4 %uint_1\n")); -} - TEST_F(ValidateMemory, VulkanRTAInsideArrayWithRuntimeDescriptorArrayBad) { std::string spirv = R"( OpCapability RuntimeDescriptorArrayEXT @@ -3843,9 +3454,10 @@ OpFunctionEnd } using ValidateSizedVariable = - spvtest::ValidateBase<std::tuple<std::string, std::string, std::string>>; + spvtest::ValidateBase<std::tuple<std::string, std::string, + std::string, spv_target_env>>; -CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit) { +CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit, bool buffer_block) { CodeGenerator generator; generator.capabilities_ = "OpCapability Shader\nOpCapability Linkage\n"; generator.extensions_ = @@ -3853,20 +3465,25 @@ CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit) { "\"SPV_KHR_8bit_storage\"\n"; generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; if (is_8bit) { - generator.before_types_ = R"(OpDecorate %char_buffer_block BufferBlock -OpMemberDecorate %char_buffer_block 0 Offset 0 -)"; + generator.before_types_ = "OpMemberDecorate %char_buffer_block 0 Offset 0\n"; + if (buffer_block) + generator.before_types_ += "OpDecorate %char_buffer_block BufferBlock\n"; + generator.types_ = R"(%void = OpTypeVoid %char = OpTypeInt 8 0 %char4 = OpTypeVector %char 4 %char_buffer_block = OpTypeStruct %char )"; } else { - generator.before_types_ = R"(OpDecorate %half_buffer_block BufferBlock -OpDecorate %short_buffer_block BufferBlock -OpMemberDecorate %half_buffer_block 0 Offset 0 -OpMemberDecorate %short_buffer_block 0 Offset 0 -)"; + generator.before_types_ = + "OpMemberDecorate %half_buffer_block 0 Offset 0\n" + "OpMemberDecorate %short_buffer_block 0 Offset 0\n"; + if (buffer_block) { + generator.before_types_ += + "OpDecorate %half_buffer_block BufferBlock\n" + "OpDecorate %short_buffer_block BufferBlock\n"; + } + generator.types_ = R"(%void = OpTypeVoid %short = OpTypeInt 16 0 %half = OpTypeFloat 16 @@ -3889,6 +3506,10 @@ TEST_P(ValidateSizedVariable, Capability) { const std::string storage_class = std::get<0>(GetParam()); const std::string capability = std::get<1>(GetParam()); const std::string var_type = std::get<2>(GetParam()); + const spv_target_env target = std::get<3>(GetParam()); + + ASSERT_TRUE(target == SPV_ENV_UNIVERSAL_1_3 || + target == SPV_ENV_UNIVERSAL_1_4); bool type_8bit = false; if (var_type == "%char" || var_type == "%char4" || @@ -3896,7 +3517,16 @@ TEST_P(ValidateSizedVariable, Capability) { type_8bit = true; } - auto generator = GetSizedVariableCodeGenerator(type_8bit); + const bool buffer_block = var_type.find("buffer_block") != std::string::npos; + + auto generator = GetSizedVariableCodeGenerator(type_8bit, buffer_block); + + if (capability == "WorkgroupMemoryExplicitLayout8BitAccessKHR" || + capability == "WorkgroupMemoryExplicitLayout16BitAccessKHR") { + generator.extensions_ += + "OpExtension \"SPV_KHR_workgroup_memory_explicit_layout\"\n"; + } + generator.types_ += "%ptr_type = OpTypePointer " + storage_class + " " + var_type + "\n%var = OpVariable %ptr_type " + storage_class + "\n"; @@ -3926,7 +3556,6 @@ TEST_P(ValidateSizedVariable, Capability) { } storage_class_ok = true; } else if (storage_class == "Uniform") { - bool buffer_block = var_type.find("buffer_block") != std::string::npos; if (type_8bit) { capability_ok = capability == "UniformAndStorageBuffer8BitAccess" || (capability == "StorageBuffer8BitAccess" && buffer_block); @@ -3936,11 +3565,30 @@ TEST_P(ValidateSizedVariable, Capability) { (capability == "StorageBuffer16BitAccess" && buffer_block); } storage_class_ok = true; + } else if (storage_class == "Workgroup") { + if (type_8bit) { + capability_ok = + capability == "WorkgroupMemoryExplicitLayout8BitAccessKHR"; + } else { + capability_ok = + capability == "WorkgroupMemoryExplicitLayout16BitAccessKHR"; + } + storage_class_ok = true; } - CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); - spv_result_t result = ValidateInstructions(SPV_ENV_UNIVERSAL_1_3); - if (capability_ok) { + CompileSuccessfully(generator.Build(), target); + spv_result_t result = ValidateInstructions(target); + if (target < SPV_ENV_UNIVERSAL_1_4 && + (capability == "WorkgroupMemoryExplicitLayout8BitAccessKHR" || + capability == "WorkgroupMemoryExplicitLayout16BitAccessKHR")) { + EXPECT_EQ(SPV_ERROR_WRONG_VERSION, result); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires SPIR-V version 1.4 or later")); + } else if (buffer_block && target > SPV_ENV_UNIVERSAL_1_3) { + EXPECT_EQ(SPV_ERROR_WRONG_VERSION, result); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires SPIR-V version 1.3 or earlier")); + } else if (capability_ok) { EXPECT_EQ(SPV_SUCCESS, result); } else { EXPECT_EQ(SPV_ERROR_INVALID_ID, result); @@ -3965,8 +3613,10 @@ INSTANTIATE_TEST_SUITE_P( Combine(Values("UniformConstant", "Input", "Output", "Workgroup", "CrossWorkgroup", "Private", "StorageBuffer", "Uniform"), Values("StorageBuffer8BitAccess", - "UniformAndStorageBuffer8BitAccess", "StoragePushConstant8"), - Values("%char", "%char4", "%char_buffer_block"))); + "UniformAndStorageBuffer8BitAccess", "StoragePushConstant8", + "WorkgroupMemoryExplicitLayout8BitAccessKHR"), + Values("%char", "%char4", "%char_buffer_block"), + Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4))); INSTANTIATE_TEST_SUITE_P( Storage16, ValidateSizedVariable, @@ -3974,9 +3624,11 @@ INSTANTIATE_TEST_SUITE_P( "CrossWorkgroup", "Private", "StorageBuffer", "Uniform"), Values("StorageBuffer16BitAccess", "UniformAndStorageBuffer16BitAccess", - "StoragePushConstant16", "StorageInputOutput16"), + "StoragePushConstant16", "StorageInputOutput16", + "WorkgroupMemoryExplicitLayout16BitAccessKHR"), Values("%short", "%half", "%short4", "%half4", "%mat4x4", - "%short_buffer_block", "%half_buffer_block"))); + "%short_buffer_block", "%half_buffer_block"), + Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4))); using ValidateSizedLoadStore = spvtest::ValidateBase<std::tuple<std::string, uint32_t, std::string>>; @@ -4390,6 +4042,109 @@ OpFunctionEnd "typed as OpTypeStruct, or an array of this type")); } +TEST_F(ValidateMemory, VulkanInvariantOutputSuccess) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %var Location 0 +OpDecorate %var Invariant +%void = OpTypeVoid +%f32 = OpTypeFloat 32 +%ptr_output = OpTypePointer Output %f32 +%var = OpVariable %ptr_output Output +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateMemory, VulkanInvariantInputStructSuccess) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %var Location 0 +OpMemberDecorate %struct 1 Invariant +%void = OpTypeVoid +%f32 = OpTypeFloat 32 +%struct = OpTypeStruct %f32 %f32 +%ptr_input = OpTypePointer Input %struct +%var = OpVariable %ptr_input Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateMemory, VulkanInvariantWrongStorageClass) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %var Invariant +%void = OpTypeVoid +%f32 = OpTypeFloat 32 +%ptr_private = OpTypePointer Private %f32 +%var = OpVariable %ptr_private Private +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Invariant-04677")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Variable decorated with Invariant must only be identified with the " + "Input or Output storage class in Vulkan environment.")); +} + +TEST_F(ValidateMemory, VulkanInvariantMemberWrongStorageClass) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpMemberDecorate %struct 1 Invariant +%void = OpTypeVoid +%f32 = OpTypeFloat 32 +%struct = OpTypeStruct %f32 %f32 +%ptr_private = OpTypePointer Private %struct +%var = OpVariable %ptr_private Private +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Invariant-04677")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable struct member decorated with Invariant must " + "only be identified with the Input or Output storage " + "class in Vulkan environment.")); +} + TEST_F(ValidateMemory, PhysicalStorageBufferPtrEqual) { const std::string spirv = R"( OpCapability Shader @@ -4480,6 +4235,55 @@ OpFunctionEnd "Cannot use a pointer in the PhysicalStorageBuffer storage class")); } +TEST_F(ValidateMemory, VulkanInitializerWithWorkgroupStorageClassBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Workgroup %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Workgroup %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable initializers in Workgroup storage class are " + "limited to OpConstantNull")); +} + +TEST_F(ValidateMemory, VulkanInitializerWithWorkgroupStorageClassGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Workgroup %float +%init_val = OpConstantNull %float +%1 = OpVariable %float_ptr Workgroup %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_misc_test.cpp b/test/val/val_misc_test.cpp index e181f8d1..499b5b28 100644 --- a/test/val/val_misc_test.cpp +++ b/test/val/val_misc_test.cpp @@ -227,6 +227,30 @@ OpFunctionEnd)"; HasSubstr("Scope must be Subgroup or Device")); } +TEST_F(ValidateMisc, VulkanShaderClockWorkgroupScope) { + const std::string spirv = ShaderClockSpriv + R"( +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%uint = OpTypeInt 32 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong +%workgroup = OpConstant %uint 2 +%uint_1 = OpConstant %uint 1 +%main = OpFunction %void None %3 +%5 = OpLabel +%time1 = OpVariable %_ptr_Function_ulong Function +%11 = OpReadClockKHR %ulong %workgroup +OpStore %time1 %11 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpReadClockKHR-04652")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Scope must be Subgroup or Device")); +} + TEST_F(ValidateMisc, UndefVoid) { const std::string spirv = R"( OpCapability Shader diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index 0a1476ee..99f5c9cf 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp @@ -62,6 +62,8 @@ OpEntryPoint GLCompute %main "main" spv_target_env env = SPV_ENV_VULKAN_1_0; CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-LocalSize-04683")); EXPECT_THAT( getDiagnosticString(), HasSubstr("In the Vulkan environment, GLCompute execution model entry " @@ -111,6 +113,8 @@ OpExecutionMode %main OriginLowerLeft CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OriginLowerLeft-04653")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("In the Vulkan environment, the OriginLowerLeft " "execution mode must not be used.")); } @@ -128,6 +132,8 @@ OpExecutionMode %main PixelCenterInteger CompileSuccessfully(spirv, env); EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PixelCenterInteger-04654")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("In the Vulkan environment, the PixelCenterInteger " "execution mode must not be used.")); } @@ -531,24 +537,16 @@ TEST_P(ValidateModeExecution, ExecutionMode) { std::ostringstream sstr; sstr << "OpCapability Shader\n"; - if (!spvIsWebGPUEnv(env)) { - sstr << "OpCapability Geometry\n"; - sstr << "OpCapability Tessellation\n"; - sstr << "OpCapability TransformFeedback\n"; - } - if (!spvIsVulkanOrWebGPUEnv(env)) { + sstr << "OpCapability Geometry\n"; + sstr << "OpCapability Tessellation\n"; + sstr << "OpCapability TransformFeedback\n"; + if (!spvIsVulkanEnv(env)) { sstr << "OpCapability Kernel\n"; if (env == SPV_ENV_UNIVERSAL_1_3) { sstr << "OpCapability SubgroupDispatch\n"; } } - if (spvIsWebGPUEnv(env)) { - sstr << "OpCapability VulkanMemoryModelKHR\n"; - sstr << "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"; - sstr << "OpMemoryModel Logical VulkanKHR\n"; - } else { - sstr << "OpMemoryModel Logical GLSL450\n"; - } + sstr << "OpMemoryModel Logical GLSL450\n"; sstr << "OpEntryPoint " << model << " %main \"main\"\n"; if (mode.find("LocalSizeId") == 0 || mode.find("LocalSizeHintId") == 0 || mode.find("SubgroupsPerWorkgroupId") == 0) { @@ -714,39 +712,6 @@ INSTANTIATE_TEST_SUITE_P( "SubgroupsPerWorkgroup 1", "SubgroupsPerWorkgroupId %int1"), Values(SPV_ENV_UNIVERSAL_1_3))); -INSTANTIATE_TEST_SUITE_P(ValidateModeGLComputeWebGPUAllowListGood, - ValidateModeExecution, - Combine(Values(SPV_SUCCESS), Values(""), - Values("GLCompute"), Values("LocalSize 1 1 1"), - Values(SPV_ENV_WEBGPU_0))); - -INSTANTIATE_TEST_SUITE_P( - ValidateModeGLComputeWebGPUAllowListBad, ValidateModeExecution, - Combine(Values(SPV_ERROR_INVALID_DATA), - Values("Execution mode must be one of OriginUpperLeft, " - "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, " - "LocalSize, or LocalSizeHint for WebGPU environment"), - Values("GLCompute"), Values("LocalSizeId %int1 %int1 %int1"), - Values(SPV_ENV_WEBGPU_0))); - -INSTANTIATE_TEST_SUITE_P( - ValidateModeFragmentWebGPUAllowListGood, ValidateModeExecution, - Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"), - Values("OriginUpperLeft", "DepthReplacing", "DepthGreater", - "DepthLess", "DepthUnchanged"), - Values(SPV_ENV_WEBGPU_0))); - -INSTANTIATE_TEST_SUITE_P( - ValidateModeFragmentWebGPUAllowListBad, ValidateModeExecution, - Combine(Values(SPV_ERROR_INVALID_DATA), - Values("Execution mode must be one of OriginUpperLeft, " - "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, " - "LocalSize, or LocalSizeHint for WebGPU environment"), - Values("Fragment"), - Values("PixelCenterInteger", "OriginLowerLeft", - "EarlyFragmentTests"), - Values(SPV_ENV_WEBGPU_0))); - TEST_F(ValidateModeExecution, MeshNVLocalSize) { const std::string spirv = R"( OpCapability Shader diff --git a/test/val/val_non_uniform_test.cpp b/test/val/val_non_uniform_test.cpp index fbd11a9e..3840eec6 100644 --- a/test/val/val_non_uniform_test.cpp +++ b/test/val/val_non_uniform_test.cpp @@ -100,6 +100,7 @@ OpFunctionEnd)"; SpvScope scopes[] = {SpvScopeCrossDevice, SpvScopeDevice, SpvScopeWorkgroup, SpvScopeSubgroup, SpvScopeInvocation}; +using ValidateGroupNonUniform = spvtest::ValidateBase<bool>; using GroupNonUniform = spvtest::ValidateBase< std::tuple<std::string, std::string, SpvScope, std::string, std::string>>; @@ -140,6 +141,8 @@ TEST_P(GroupNonUniform, Vulkan1p1) { EXPECT_EQ(SPV_SUCCESS, result); } else { EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04642")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -288,6 +291,41 @@ INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitCountBadValue, GroupNonUniform, Values("Expected Value to be a vector of four " "components of integer type scalar"))); +TEST_F(ValidateGroupNonUniform, VulkanGroupNonUniformBallotBitCountOperation) { + std::string test = R"( +OpCapability Shader +OpCapability GroupNonUniform +OpCapability GroupNonUniformBallot +OpCapability GroupNonUniformClustered +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%func = OpTypeFunction %void +%u32 = OpTypeInt 32 0 +%u32vec4 = OpTypeVector %u32 4 +%u32_0 = OpConstant %u32 0 +%u32vec4_null = OpConstantComposite %u32vec4 %u32_0 %u32_0 %u32_0 %u32_0 +%subgroup = OpConstant %u32 3 +%main = OpFunction %void None %func +%main_entry = OpLabel +%result = OpGroupNonUniformBallotBitCount %u32 %subgroup ClusteredReduce %u32vec4_null +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(test, SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In Vulkan: The OpGroupNonUniformBallotBitCount group operation must " + "be only: Reduce, InclusiveScan, or ExclusiveScan.")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_opencl_test.cpp b/test/val/val_opencl_test.cpp index 10641587..9dab931a 100644 --- a/test/val/val_opencl_test.cpp +++ b/test/val/val_opencl_test.cpp @@ -23,6 +23,7 @@ namespace spvtools { namespace val { namespace { +using testing::Eq; using testing::HasSubstr; using ValidateOpenCL = spvtest::ValidateBase<bool>; @@ -91,7 +92,7 @@ TEST_F(ValidateOpenCL, NonZeroMSImageBad) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); EXPECT_THAT( getDiagnosticString(), - HasSubstr("MS must be 0 in the OpenCL environement." + HasSubstr("MS must be 0 in the OpenCL environment." "\n %2 = OpTypeImage %void 2D 0 0 1 0 Unknown ReadOnly\n")); } @@ -224,6 +225,264 @@ TEST_F(ValidateOpenCL, ImageReadWithConstOffsetBad) { "\n %call = OpImageRead %v4uint %img %coord ConstOffset %coord\n")); } +TEST_F(ValidateOpenCL, ImageRead_NonDepthScalarFloatResult_Bad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint + %void = OpTypeVoid + %float = OpTypeFloat 32 + %3 = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %float %img %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateOpenCL, ImageRead_NonDepthScalarIntResult_Bad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint + %void = OpTypeVoid + %float = OpTypeFloat 32 + %3 = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %uint %img %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateOpenCL, ImageRead_NonDepthVector3FloatResult_Bad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint + %void = OpTypeVoid + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %3 = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %v3float %img %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateOpenCL, ImageRead_NonDepthVector4FloatResult_Ok) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint + %void = OpTypeVoid + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %3 = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %v4float %img %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateOpenCL, ImageRead_NonDepthVector4IntResult_Ok) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint + %void = OpTypeVoid + %v4uint = OpTypeVector %uint 4 + %3 = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %v4uint %img %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateOpenCL, ImageRead_DepthScalarFloatResult_Ok) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint + %void = OpTypeVoid + %float = OpTypeFloat 32 + %3 = OpTypeImage %void 2D 1 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %float %img %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateOpenCL, ImageRead_DepthScalarIntResult_Bad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint + %void = OpTypeVoid + %float = OpTypeFloat 32 + %3 = OpTypeImage %void 2D 1 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %uint %img %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type from a depth image " + "read to result in a scalar float value")); +} + +TEST_F(ValidateOpenCL, ImageRead_DepthVectorFloatResult_Bad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint + %void = OpTypeVoid + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %3 = OpTypeImage %void 2D 1 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %v4float %img %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type from a depth image " + "read to result in a scalar float value")); +} + TEST_F(ValidateOpenCL, ImageSampleExplicitLodWithConstOffsetBad) { std::string spirv = R"( OpCapability Addresses @@ -236,18 +495,16 @@ TEST_F(ValidateOpenCL, ImageSampleExplicitLodWithConstOffsetBad) { OpName %coord "coord" OpName %call "call" %uint = OpTypeInt 32 0 - %uint_7 = OpConstant %uint 7 - %uint_3 = OpConstant %uint 3 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantNull %v2uint %void = OpTypeVoid %3 = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly %4 = OpTypeFunction %void %3 %8 = OpTypeSampler %10 = OpTypeSampledImage %3 %v4uint = OpTypeVector %uint 4 - %v2uint = OpTypeVector %uint 2 %float = OpTypeFloat 32 %9 = OpConstantSampler %8 None 0 Nearest - %coord = OpConstantComposite %v2uint %uint_7 %uint_3 %float_0 = OpConstant %float 0 %5 = OpFunction %void None %4 %6 = OpFunctionParameter %3 diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp index fe37a93c..e6f98bff 100644 --- a/test/val/val_storage_test.cpp +++ b/test/val/val_storage_test.cpp @@ -250,70 +250,6 @@ TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParamBad) { HasSubstr("OpFunctionCall Argument <id> '")); } -std::string GetVarDeclStr(const std::string& storage_class) { - if (storage_class != "Output" && storage_class != "Private" && - storage_class != "Function") { - return "%var = OpVariable %ptrt " + storage_class + "\n"; - } else { - return "%var = OpVariable %ptrt " + storage_class + " %null\n"; - } -} - -TEST_P(ValidateStorageClass, WebGPU) { - std::string storage_class = std::get<0>(GetParam()); - bool is_local = std::get<1>(GetParam()); - bool is_valid = std::get<2>(GetParam()); - std::string error = std::get<3>(GetParam()); - - std::string str = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Fragment %func "func" - OpExecutionMode %func OriginUpperLeft -%intt = OpTypeInt 32 1 -%voidt = OpTypeVoid -%vfunct = OpTypeFunction %voidt -%null = OpConstantNull %intt -)"; - str += "%ptrt = OpTypePointer " + storage_class + " %intt\n"; - if (!is_local) str += GetVarDeclStr(storage_class); - str += R"( -%func = OpFunction %voidt None %vfunct -%funcl = OpLabel -)"; - if (is_local) str += GetVarDeclStr(storage_class); - str += R"( -OpReturn -OpFunctionEnd -)"; - - CompileSuccessfully(str, SPV_ENV_WEBGPU_0); - if (is_valid) { - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); - } else { - ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); - } -} - -INSTANTIATE_TEST_SUITE_P( - StorageClass, ValidateStorageClass, - Values(std::make_tuple("UniformConstant", false, true, ""), - std::make_tuple("Uniform", false, true, ""), - std::make_tuple("StorageBuffer", false, true, ""), - std::make_tuple("Input", false, true, ""), - std::make_tuple("Output", false, true, ""), - std::make_tuple("Image", false, true, ""), - std::make_tuple("Workgroup", false, true, ""), - std::make_tuple("Private", false, true, ""), - std::make_tuple("Function", true, true, ""), - std::make_tuple("CrossWorkgroup", false, false, - "Invalid storage class for target environment"), - std::make_tuple("PushConstant", false, false, - "Invalid storage class for target environment"))); - } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_validation_state_test.cpp b/test/val/val_validation_state_test.cpp index 45815790..7a38d3a1 100644 --- a/test/val/val_validation_state_test.cpp +++ b/test/val/val_validation_state_test.cpp @@ -245,12 +245,6 @@ TEST_F(ValidationStateTest, CheckVulkanNonRecursiveBodyGood) { ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); } -TEST_F(ValidationStateTest, CheckWebGPUNonRecursiveBodyGood) { - std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody; - CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); -} - TEST_F(ValidationStateTest, CheckDirectlyRecursiveBodyGood) { std::string spirv = std::string(kHeader) + kDirectlyRecursiveBody; CompileSuccessfully(spirv); @@ -267,16 +261,6 @@ TEST_F(ValidationStateTest, CheckVulkanDirectlyRecursiveBodyBad) { " %1 = OpFunction %void Pure|Const %3\n")); } -TEST_F(ValidationStateTest, CheckWebGPUDirectlyRecursiveBodyBad) { - std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody; - CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, - ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Entry points may not have a call graph with cycles.\n " - " %1 = OpFunction %void Pure|Const %3\n")); -} - TEST_F(ValidationStateTest, CheckIndirectlyRecursiveBodyGood) { std::string spirv = std::string(kHeader) + kIndirectlyRecursiveBody; CompileSuccessfully(spirv); @@ -294,68 +278,6 @@ TEST_F(ValidationStateTest, CheckVulkanIndirectlyRecursiveBodyBad) { " %1 = OpFunction %void Pure|Const %3\n")); } -// Indirectly recursive functions are caught by the function definition layout -// rules, because they cause a situation where there are 2 functions that have -// to be before each other, and layout is checked earlier. -TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) { - std::string spirv = - std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody; - CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, - ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, functions need to be defined before being " - "called.\n %10 = OpFunctionCall %_struct_5 %11\n")); -} - -TEST_F(ValidationStateTest, - CheckWebGPUDuplicateEntryNamesDifferentFunctionsBad) { - std::string spirv = std::string(kVulkanMemoryHeader) + R"( -OpEntryPoint Fragment %func_1 "main" -OpEntryPoint Vertex %func_2 "main" -OpExecutionMode %func_1 OriginUpperLeft -%void = OpTypeVoid -%void_f = OpTypeFunction %void -%func_1 = OpFunction %void None %void_f -%label_1 = OpLabel - OpReturn - OpFunctionEnd -%func_2 = OpFunction %void None %void_f -%label_2 = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, - ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Entry point name \"main\" is not unique, which is not allow " - "in WebGPU env.\n %1 = OpFunction %void None %4\n")); -} - -TEST_F(ValidationStateTest, CheckWebGPUDuplicateEntryNamesSameFunctionBad) { - std::string spirv = std::string(kVulkanMemoryHeader) + R"( -OpEntryPoint GLCompute %func_1 "main" -OpEntryPoint Vertex %func_1 "main" -%void = OpTypeVoid -%void_f = OpTypeFunction %void -%func_1 = OpFunction %void None %void_f -%label_1 = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, - ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("Entry point name \"main\" is not unique, which is not allow " - "in WebGPU env.\n %1 = OpFunction %void None %3\n")); -} - } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_version_test.cpp b/test/val/val_version_test.cpp index 2b9542a1..98565ddb 100644 --- a/test/val/val_version_test.cpp +++ b/test/val/val_version_test.cpp @@ -40,21 +40,6 @@ OpReturn OpFunctionEnd )"; -const std::string webgpu_spirv = R"( -OpCapability Shader -OpCapability VulkanMemoryModelKHR -OpExtension "SPV_KHR_vulkan_memory_model" -OpMemoryModel Logical VulkanKHR -OpEntryPoint Fragment %func "func" -OpExecutionMode %func OriginUpperLeft -%void = OpTypeVoid -%functy = OpTypeFunction %void -%func = OpFunction %void None %functy -%1 = OpLabel -OpReturn -OpFunctionEnd -)"; - const std::string opencl_spirv = R"( OpCapability Addresses OpCapability Kernel @@ -85,7 +70,6 @@ std::string version(spv_target_env env) { return "1.2"; case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_VULKAN_1_1: - case SPV_ENV_WEBGPU_0: return "1.3"; case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: @@ -126,7 +110,6 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true), - std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_WEBGPU_0, webgpu_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), @@ -139,7 +122,6 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), - std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_WEBGPU_0, webgpu_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), @@ -152,7 +134,6 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), - std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_WEBGPU_0, webgpu_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), @@ -164,8 +145,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), - std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), - std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_WEBGPU_0, webgpu_spirv, true) + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) ) ); diff --git a/test/val/val_webgpu_test.cpp b/test/val/val_webgpu_test.cpp deleted file mode 100644 index 62fa6a7a..00000000 --- a/test/val/val_webgpu_test.cpp +++ /dev/null @@ -1,424 +0,0 @@ -// 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. - -// Validation tests for WebGPU env specific checks - -#include <string> - -#include "gmock/gmock.h" -#include "test/val/val_fixtures.h" - -namespace spvtools { -namespace val { -namespace { - -using testing::HasSubstr; - -using ValidateWebGPU = spvtest::ValidateBase<bool>; - -TEST_F(ValidateWebGPU, OpUndefIsDisallowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" -%float = OpTypeFloat 32 -%1 = OpUndef %float -%void = OpTypeVoid -%void_f = OpTypeFunction %void -%func = OpFunction %void None %void_f -%label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - - // Control case: OpUndef is allowed in SPIR-V 1.3 - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); - - // Control case: OpUndef is disallowed in the WebGPU env - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed")); -} - -TEST_F(ValidateWebGPU, OpNameIsAllowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" - OpName %1 "foo" - %1 = OpTypeFloat 32 - %void = OpTypeVoid - %void_f = OpTypeFunction %void - %func = OpFunction %void None %void_f - %label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" - OpMemberName %2 0 "foo" - %1 = OpTypeFloat 32 - %2 = OpTypeStruct %1 - %void = OpTypeVoid - %void_f = OpTypeFunction %void - %func = OpFunction %void None %void_f - %label = OpLabel - OpReturn - OpFunctionEnd - -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, OpSourceIsAllowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" - OpSource GLSL 450 - %void = OpTypeVoid - %void_f = OpTypeFunction %void - %func = OpFunction %void None %void_f - %label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" - OpSource GLSL 450 - OpSourceContinued "I am a happy shader! Yay! ;" - %void = OpTypeVoid - %void_f = OpTypeFunction %void - %func = OpFunction %void None %void_f - %label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" - OpSourceExtension "bar" - %void = OpTypeVoid - %void_f = OpTypeFunction %void - %func = OpFunction %void None %void_f - %label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, OpStringIsAllowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" - %1 = OpString "foo" - %void = OpTypeVoid - %void_f = OpTypeFunction %void - %func = OpFunction %void None %void_f - %label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, OpLineIsAllowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" - %1 = OpString "minimal.vert" - OpLine %1 1 1 - %void = OpTypeVoid - %void_f = OpTypeFunction %void - %func = OpFunction %void None %void_f - %label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, OpNoLineIsAllowed) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" - OpNoLine - %void = OpTypeVoid - %void_f = OpTypeFunction %void - %func = OpFunction %void None %void_f - %label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" -%void = OpTypeVoid -%void_f = OpTypeFunction %void -%func = OpFunction %void None %void_f -%label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, LogicalAddressingGLSL450MemoryGood) { - std::string spirv = R"( - OpCapability Shader - OpMemoryModel Logical GLSL450 - OpEntryPoint Vertex %func "shader" -%void = OpTypeVoid -%void_f = OpTypeFunction %void -%func = OpFunction %void None %void_f -%label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, LogicalAddressingSimpleMemoryGood) { - std::string spirv = R"( - OpCapability Shader - OpMemoryModel Logical Simple - OpEntryPoint Vertex %func "shader" -%void = OpTypeVoid -%void_f = OpTypeFunction %void -%func = OpFunction %void None %void_f -%label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, KernelIsBad) { - std::string spirv = R"( - OpCapability Kernel - OpMemoryModel Logical Simple - OpNoLine -)"; - - CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Capability Kernel is not allowed by WebGPU " - "specification (or requires extension)")); -} - -TEST_F(ValidateWebGPU, OpenCLMemoryModelBad) { - std::string spirv = R"( - OpCapability Shader - OpMemoryModel Logical OpenCL - OpNoLine -)"; - - CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, - ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Operand 2 of MemoryModel requires one of these " - "capabilities: Kernel")); -} - -TEST_F(ValidateWebGPU, AllowListedExtendedInstructionsImportGood) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" -%1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical VulkanKHR - OpEntryPoint Vertex %func "shader" -%void = OpTypeVoid -%void_f = OpTypeFunction %void -%func = OpFunction %void None %void_f -%label = OpLabel - OpReturn - OpFunctionEnd -)"; - - CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, NonAllowListedExtendedInstructionsImportBad) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_vulkan_memory_model" -%1 = OpExtInstImport "OpenCL.std" - OpMemoryModel Logical VulkanKHR -)"; - - CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, the only valid parameter to " - "OpExtInstImport is \"GLSL.std.450\".\n %1 = " - "OpExtInstImport \"OpenCL.std\"\n")); -} - -TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelExtensionBad) { - std::string spirv = R"( - OpCapability Shader - OpCapability VulkanMemoryModelKHR - OpExtension "SPV_KHR_8bit_storage" - OpExtension "SPV_KHR_vulkan_memory_model" - OpMemoryModel Logical VulkanKHR -)"; - - CompileSuccessfully(spirv); - - EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("For WebGPU, the only valid parameter to OpExtension " - "is \"SPV_KHR_vulkan_memory_model\".\n OpExtension " - "\"SPV_KHR_8bit_storage\"\n")); -} - -spv_binary GenerateTrivialBinary(bool need_little_endian) { - // Smallest possible valid WebGPU SPIR-V binary in little endian. Contains all - // the required boilerplate and a trivial entry point function. - static const uint8_t binary_bytes[] = { - // clang-format off - 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0xE1, 0x14, 0x00, 0x00, - 0x0A, 0x00, 0x08, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F, - 0x76, 0x75, 0x6C, 0x6B, 0x61, 0x6E, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, - 0x79, 0x5F, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x00, 0x0E, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64, - 0x65, 0x72, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 - // clang-format on - }; - static const size_t word_count = sizeof(binary_bytes) / sizeof(uint32_t); - std::unique_ptr<spv_binary_t> result(new spv_binary_t); - if (!result) return nullptr; - - result->wordCount = word_count; - result->code = new uint32_t[word_count]; - if (!result->code) return nullptr; - - if (need_little_endian) { - memcpy(result->code, binary_bytes, sizeof(binary_bytes)); - } else { - uint8_t* code_bytes = reinterpret_cast<uint8_t*>(result->code); - for (size_t word = 0; word < word_count; ++word) { - code_bytes[4 * word] = binary_bytes[4 * word + 3]; - code_bytes[4 * word + 1] = binary_bytes[4 * word + 2]; - code_bytes[4 * word + 2] = binary_bytes[4 * word + 1]; - code_bytes[4 * word + 3] = binary_bytes[4 * word]; - } - } - - return result.release(); -} - -TEST_F(ValidateWebGPU, LittleEndianGood) { - DestroyBinary(); - binary_ = GenerateTrivialBinary(true); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); -} - -TEST_F(ValidateWebGPU, BigEndianBad) { - DestroyBinary(); - binary_ = GenerateTrivialBinary(false); - EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("WebGPU requires SPIR-V to be little endian.")); -} - -} // namespace -} // namespace val -} // namespace spvtools diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp index 82d430eb..1956f595 100644 --- a/tools/link/linker.cpp +++ b/tools/link/linker.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "spirv-tools/linker.hpp" + #include <cstring> #include <iostream> #include <vector> @@ -19,42 +21,56 @@ #include "source/spirv_target_env.h" #include "source/table.h" #include "spirv-tools/libspirv.hpp" -#include "spirv-tools/linker.hpp" #include "tools/io.h" -void print_usage(char* argv0) { - std::string target_env_list = spvTargetEnvList(27, 95); +namespace { + +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; + +void print_usage(const char* program) { + std::string target_env_list = spvTargetEnvList(16, 80); + // NOTE: Please maintain flags in lexicographical order. printf( R"(%s - Link SPIR-V binary files together. -USAGE: %s [options] <filename> [<filename> ...] +USAGE: %s [options] [-o <output>] <input>... -The SPIR-V binaries are read from the different <filename>. +The SPIR-V binaries are read from the different <input>(s). +The SPIR-V resulting linked binary module is written to the file "out.spv" +unless the -o option is used; if <output> is "-", it is written to the standard +output. NOTE: The linker is a work in progress. -Options: - -h, --help Print this help. - -o Name of the resulting linked SPIR-V binary. - --create-library Link the binaries into a library, keeping all exported symbols. - --allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved. - --verify-ids Verify that IDs in the resulting modules are truly unique. - --version Display linker version information - --target-env {%s} - Use validation rules from the specified environment. +Options (in lexicographical order): + --allow-partial-linkage + Allow partial linkage by accepting imported symbols to be + unresolved. + --create-library + Link the binaries into a library, keeping all exported symbols. + -h, --help + Print this help. + --target-env <env> + Set the target environment. Without this flag the target + environment defaults to spv1.5. <env> must be one of + {%s} + --verify-ids + Verify that IDs in the resulting modules are truly unique. + --version + Display linker version information )", - argv0, argv0, target_env_list.c_str()); + program, program, target_env_list.c_str()); } +} // namespace + int main(int argc, char** argv) { std::vector<const char*> inFiles; const char* outFile = nullptr; - spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0; + spv_target_env target_env = kDefaultEnvironment; spvtools::LinkerOptions options; - bool continue_processing = true; - int return_code = 0; - for (int argi = 1; continue_processing && argi < argc; ++argi) { + for (int argi = 1; argi < argc; ++argi) { const char* cur_arg = argv[argi]; if ('-' == cur_arg[0]) { if (0 == strcmp(cur_arg, "-o")) { @@ -63,55 +79,48 @@ int main(int argc, char** argv) { outFile = argv[++argi]; } else { fprintf(stderr, "error: More than one output file specified\n"); - continue_processing = false; - return_code = 1; + return 1; } } else { fprintf(stderr, "error: Missing argument to %s\n", cur_arg); - continue_processing = false; - return_code = 1; + return 1; } - } else if (0 == strcmp(cur_arg, "--create-library")) { - options.SetCreateLibrary(true); - } else if (0 == strcmp(cur_arg, "--verify-ids")) { - options.SetVerifyIds(true); } else if (0 == strcmp(cur_arg, "--allow-partial-linkage")) { options.SetAllowPartialLinkage(true); - } else if (0 == strcmp(cur_arg, "--version")) { - printf("%s\n", spvSoftwareVersionDetailsString()); - // TODO(dneto): Add OpenCL 2.2 at least. - printf("Targets:\n %s\n %s\n %s\n", - spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), - spvTargetEnvDescription(SPV_ENV_VULKAN_1_0), - spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2)); - continue_processing = false; - return_code = 0; + } else if (0 == strcmp(cur_arg, "--create-library")) { + options.SetCreateLibrary(true); } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { print_usage(argv[0]); - continue_processing = false; - return_code = 0; + return 0; } else if (0 == strcmp(cur_arg, "--target-env")) { if (argi + 1 < argc) { const auto env_str = argv[++argi]; if (!spvParseTargetEnv(env_str, &target_env)) { fprintf(stderr, "error: Unrecognized target env: %s\n", env_str); - continue_processing = false; - return_code = 1; + return 1; } } else { fprintf(stderr, "error: Missing argument to --target-env\n"); - continue_processing = false; - return_code = 1; + return 1; } + } else if (0 == strcmp(cur_arg, "--verify-ids")) { + options.SetVerifyIds(true); + } else if (0 == strcmp(cur_arg, "--version")) { + printf("%s\n", spvSoftwareVersionDetailsString()); + printf("Target: %s\n", spvTargetEnvDescription(target_env)); + return 0; + } else { + fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]); + print_usage(argv[0]); + return 1; } } else { inFiles.push_back(cur_arg); } } - // Exit if command line parsing was not successful. - if (!continue_processing) { - return return_code; + if (!outFile) { + outFile = "out.spv"; } if (inFiles.empty()) { diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 66f92286..6999b39a 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -79,18 +79,6 @@ std::string GetSizePasses() { return GetListOfPassesAsString(optimizer); } -std::string GetVulkanToWebGPUPasses() { - spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1); - optimizer.RegisterVulkanToWebGPUPasses(); - return GetListOfPassesAsString(optimizer); -} - -std::string GetWebGPUToVulkanPasses() { - spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0); - optimizer.RegisterWebGPUToVulkanPasses(); - return GetListOfPassesAsString(optimizer); -} - void PrintUsage(const char* program) { std::string target_env_list = spvTargetEnvList(16, 80); // NOTE: Please maintain flags in lexicographical order. @@ -239,10 +227,6 @@ Options (in lexicographical order):)", values, providing guarantees that satisfy Vulkan's robustBufferAccess rules.)"); printf(R"( - --generate-webgpu-initializers - Adds initial values to OpVariable instructions that are missing - them, due to their storage type requiring them for WebGPU.)"); - printf(R"( --if-conversion Convert if-then-else like assignments into OpSelect.)"); printf(R"( @@ -261,11 +245,6 @@ Options (in lexicographical order):)", option --relax-logical-pointer to the validator.)", GetLegalizationPasses().c_str()); printf(R"( - --legalize-vector-shuffle - Converts any usages of 0xFFFFFFFF for the literals in - OpVectorShuffle to a literal 0. This is done since 0xFFFFFFFF is - forbidden in WebGPU.)"); - printf(R"( --local-redundancy-elimination Looks for instructions in the same basic block that compute the same value, and deletes the redundant ones.)"); @@ -463,13 +442,6 @@ Options (in lexicographical order):)", Forwards this option to the validator. See the validator help for details.)"); printf(R"( - --split-invalid-unreachable - Attempts to legalize for WebGPU cases where an unreachable - merge-block is also a continue-target by splitting it into two - separate blocks. There exist legal, for Vulkan, instances of this - pattern that cannot be converted into legal WebGPU, so this - conversion may not succeed.)"); - printf(R"( --skip-validation Will not validate the SPIR-V before optimizing. If the SPIR-V is invalid, the optimizer may fail or generate incorrect code. @@ -513,39 +485,15 @@ Options (in lexicographical order):)", 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.)"); printf(R"( - --vulkan-to-webgpu - Turns on the prescribed passes for converting from Vulkan to - WebGPU and sets the target environment to webgpu0. Other passes - may be turned on via additional flags, but such combinations are - not tested. - Using --target-env with this flag is not allowed. - - This flag is the equivalent of passing in --target-env=webgpu0 - and specifying the following optimization code names: - %s - - NOTE: This flag is a WIP and its behaviour is subject to change.)", - GetVulkanToWebGPUPasses().c_str()); - printf(R"( - --webgpu-to-vulkan - Turns on the prescribed passes for converting from WebGPU to - Vulkan and sets the target environment to vulkan1.1. Other passes - may be turned on via additional flags, but such combinations are - not tested. - Using --target-env with this flag is not allowed. - - This flag is the equivalent of passing in --target-env=vulkan1.1 - and specifying the following optimization code names: - %s - - NOTE: This flag is a WIP and its behaviour is subject to change.)", - GetWebGPUToVulkanPasses().c_str()); - printf(R"( --workaround-1209 Rewrites instructions for which there are known driver bugs to avoid triggering those bugs. Current workarounds: Avoid OpUnreachable in loops.)"); printf(R"( + --workgroup-scalar-block-layout + Forwards this option to the validator. See the validator help + for details.)"); + printf(R"( --wrap-opkill Replaces all OpKill instructions in functions that can be called from a continue construct with a function call to a function @@ -714,9 +662,6 @@ OptStatus ParseFlags(int argc, const char** argv, spvtools::ValidatorOptions* validator_options, spvtools::OptimizerOptions* optimizer_options) { std::vector<std::string> pass_flags; - bool target_env_set = false; - bool vulkan_to_webgpu_set = false; - bool webgpu_to_vulkan_set = false; for (int argi = 1; argi < argc; ++argi) { const char* cur_arg = argv[argi]; if ('-' == cur_arg[0]) { @@ -781,19 +726,6 @@ OptStatus ParseFlags(int argc, const char** argv, max_id_bound); } else if (0 == strncmp(cur_arg, "--target-env=", sizeof("--target-env=") - 1)) { - target_env_set = true; - if (vulkan_to_webgpu_set) { - spvtools::Error(opt_diagnostic, nullptr, {}, - "--vulkan-to-webgpu defines the target environment, " - "so --target-env cannot be set at the same time"); - return {OPT_STOP, 1}; - } - if (webgpu_to_vulkan_set) { - spvtools::Error(opt_diagnostic, nullptr, {}, - "--webgpu-to-vulkan defines the target environment, " - "so --target-env cannot be set at the same time"); - return {OPT_STOP, 1}; - } const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); const auto target_env_str = split_flag.second.c_str(); spv_target_env target_env; @@ -803,42 +735,6 @@ OptStatus ParseFlags(int argc, const char** argv, return {OPT_STOP, 1}; } optimizer->SetTargetEnv(target_env); - } else if (0 == strcmp(cur_arg, "--vulkan-to-webgpu")) { - vulkan_to_webgpu_set = true; - if (target_env_set) { - spvtools::Error(opt_diagnostic, nullptr, {}, - "--vulkan-to-webgpu defines the target environment, " - "so --target-env cannot be set at the same time"); - return {OPT_STOP, 1}; - } - if (webgpu_to_vulkan_set) { - spvtools::Error(opt_diagnostic, nullptr, {}, - "Cannot use both --webgpu-to-vulkan and " - "--vulkan-to-webgpu at the same time, invoke twice " - "if you are wanting to go to and from"); - return {OPT_STOP, 1}; - } - - optimizer->SetTargetEnv(SPV_ENV_VULKAN_1_1); - optimizer->RegisterVulkanToWebGPUPasses(); - } else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) { - webgpu_to_vulkan_set = true; - if (target_env_set) { - spvtools::Error(opt_diagnostic, nullptr, {}, - "--webgpu-to-vulkan defines the target environment, " - "so --target-env cannot be set at the same time"); - return {OPT_STOP, 1}; - } - if (vulkan_to_webgpu_set) { - spvtools::Error(opt_diagnostic, nullptr, {}, - "Cannot use both --webgpu-to-vulkan and " - "--vulkan-to-webgpu at the same time, invoke twice " - "if you are wanting to go to and from"); - return {OPT_STOP, 1}; - } - - optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0); - optimizer->RegisterWebGPUToVulkanPasses(); } else if (0 == strcmp(cur_arg, "--validate-after-all")) { optimizer->SetValidateAfterAll(true); } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) { @@ -849,6 +745,8 @@ OptStatus ParseFlags(int argc, const char** argv, validator_options->SetRelaxBlockLayout(true); } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) { validator_options->SetScalarBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--workgroup-scalar-block-layout")) { + validator_options->SetWorkgroupScalarBlockLayout(true); } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { validator_options->SetSkipBlockLayout(true); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock index 34a1808e..c46b9017 100644 --- a/tools/sva/yarn.lock +++ b/tools/sva/yarn.lock @@ -795,9 +795,9 @@ inherits@2: integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + version "1.3.7" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" + integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== inquirer@^6.4.1: version "6.5.2" diff --git a/tools/val/val.cpp b/tools/val/val.cpp index 19b8c776..5450023a 100644 --- a/tools/val/val.cpp +++ b/tools/val/val.cpp @@ -58,6 +58,7 @@ Options: uniform, storage buffer, and push constant layouts. Scalar layout rules are more permissive than relaxed block layout so in effect this will override the --relax-block-layout option. + --workgroup-scalar-block-layout Enable scalar block layout when checking Workgroup block layouts. --skip-block-layout Skip checking standard uniform/storage buffer layout. Overrides any --relax-block-layout or --scalar-block-layout option. --relax-struct-store Allow store from one struct type to a @@ -108,7 +109,7 @@ int main(int argc, char** argv) { printf("%s\n", spvSoftwareVersionDetailsString()); printf( "Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n " - "%s\n %s\n", + "%s\n", spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2), @@ -118,8 +119,7 @@ int main(int argc, char** argv) { spvTargetEnvDescription(SPV_ENV_OPENCL_2_2), spvTargetEnvDescription(SPV_ENV_VULKAN_1_0), spvTargetEnvDescription(SPV_ENV_VULKAN_1_1), - spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4), - spvTargetEnvDescription(SPV_ENV_WEBGPU_0)); + spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); continue_processing = false; return_code = 0; } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { @@ -149,6 +149,8 @@ int main(int argc, char** argv) { options.SetUniformBufferStandardLayout(true); } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) { options.SetScalarBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--workgroup-scalar-block-layout")) { + options.SetWorkgroupScalarBlockLayout(true); } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { options.SetSkipBlockLayout(true); } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { diff --git a/utils/check_copyright.py b/utils/check_copyright.py index 39d27cb7..b15bc206 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -39,7 +39,7 @@ AUTHORS = ['The Khronos Group Inc.', 'Stefano Milizia'] CURRENT_YEAR='2020' -YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)' +YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)' COPYRIGHT_RE = re.compile( 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS))) diff --git a/utils/git-sync-deps b/utils/git-sync-deps index 05756413..eecfbe93 100755 --- a/utils/git-sync-deps +++ b/utils/git-sync-deps @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2014 Google Inc. # # Redistribution and use in source and binary forms, with or without |