// Copyright (c) 2016 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Validation tests for Data Rules. #include <sstream> #include <string> #include <utility> #include "gmock/gmock.h" #include "test/unit_spirv.h" #include "test/val/val_fixtures.h" namespace spvtools { namespace val { namespace { using ::testing::HasSubstr; using ::testing::MatchesRegex; using ValidateData = spvtest::ValidateBase<std::pair<std::string, bool>>; std::string HeaderWith(std::string cap) { return std::string("OpCapability Shader OpCapability Linkage OpCapability ") + 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 OpMemoryModel Logical GLSL450 )"; std::string header_with_addresses = R"( OpCapability Addresses OpCapability Kernel OpCapability GenericPointer OpCapability Linkage OpMemoryModel Physical32 OpenCL )"; std::string header_with_vec16_cap = R"( OpCapability Shader OpCapability Vector16 OpCapability Linkage OpMemoryModel Logical GLSL450 )"; std::string header_with_int8 = R"( OpCapability Shader OpCapability Linkage OpCapability Int8 OpMemoryModel Logical GLSL450 )"; std::string header_with_int16 = R"( OpCapability Shader OpCapability Linkage OpCapability Int16 OpMemoryModel Logical GLSL450 )"; std::string header_with_int64 = R"( OpCapability Shader OpCapability Linkage OpCapability Int64 OpMemoryModel Logical GLSL450 )"; std::string header_with_float16 = R"( OpCapability Shader OpCapability Linkage OpCapability Float16 OpMemoryModel Logical GLSL450 )"; std::string header_with_float16_buffer = R"( OpCapability Shader OpCapability Linkage OpCapability Float16Buffer OpMemoryModel Logical GLSL450 )"; std::string header_with_float64 = R"( OpCapability Shader OpCapability Linkage OpCapability Float64 OpMemoryModel Logical GLSL450 )"; std::string invalid_comp_error = "Illegal number of components"; std::string missing_cap_error = "requires the Vector16 capability"; std::string missing_int8_cap_error = "requires the Int8 capability"; std::string missing_int16_cap_error = "requires the Int16 capability," " or an extension that explicitly enables 16-bit integers."; std::string missing_int64_cap_error = "requires the Int64 capability"; std::string missing_float16_cap_error = "requires the Float16 or Float16Buffer capability," " or an extension that explicitly enables 16-bit floating point."; std::string missing_float64_cap_error = "requires the Float64 capability"; std::string invalid_num_bits_error = "Invalid number of bits"; TEST_F(ValidateData, vec0) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 0 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_comp_error)); } TEST_F(ValidateData, vec1) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 1 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_comp_error)); } TEST_F(ValidateData, vec2) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, vec3) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 3 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, vec4) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 4 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, vec5) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 5 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_comp_error)); } TEST_F(ValidateData, vec8) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 8 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_cap_error)); } TEST_F(ValidateData, vec8_with_capability) { std::string str = header_with_vec16_cap + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 8 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, vec16) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 8 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_cap_error)); } TEST_F(ValidateData, vec16_with_capability) { std::string str = header_with_vec16_cap + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 16 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, vec15) { std::string str = header + R"( %1 = OpTypeFloat 32 %2 = OpTypeVector %1 15 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_comp_error)); } TEST_F(ValidateData, int8_good) { std::string str = header_with_int8 + "%2 = OpTypeInt 8 0"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, int8_bad) { std::string str = header + "%2 = OpTypeInt 8 1"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int8_cap_error)); } TEST_F(ValidateData, int8_with_storage_buffer_8bit_access_good) { std::string str = HeaderWith( "StorageBuffer8BitAccess " "OpExtension \"SPV_KHR_8bit_storage\"") + " %2 = OpTypeInt 8 0"; CompileSuccessfully(str.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); } TEST_F(ValidateData, int8_with_uniform_and_storage_buffer_8bit_access_good) { std::string str = HeaderWith( "UniformAndStorageBuffer8BitAccess " "OpExtension \"SPV_KHR_8bit_storage\"") + " %2 = OpTypeInt 8 0"; CompileSuccessfully(str.c_str()); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); } TEST_F(ValidateData, int8_with_storage_push_constant_8_good) { std::string str = HeaderWith( "StoragePushConstant8 " "OpExtension \"SPV_KHR_8bit_storage\"") + " %2 = OpTypeInt 8 0"; CompileSuccessfully(str.c_str()); 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()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, storage_uniform_buffer_block_16_good) { std::string str = HeaderWith( "StorageUniformBufferBlock16 " "OpExtension \"SPV_KHR_16bit_storage\"") + "%2 = OpTypeInt 16 1 %3 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, storage_uniform_16_good) { std::string str = HeaderWith("StorageUniform16 OpExtension \"SPV_KHR_16bit_storage\"") + "%2 = OpTypeInt 16 1 %3 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, storage_push_constant_16_good) { std::string str = HeaderWith( "StoragePushConstant16 " "OpExtension \"SPV_KHR_16bit_storage\"") + "%2 = OpTypeInt 16 1 %3 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, storage_input_output_16_good) { std::string str = HeaderWith( "StorageInputOutput16 " "OpExtension \"SPV_KHR_16bit_storage\"") + "%2 = OpTypeInt 16 1 %3 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, amd_gpu_shader_half_float_fetch_16_good) { std::string str = R"( OpCapability Shader OpCapability Linkage OpExtension "SPV_AMD_gpu_shader_half_float_fetch" OpMemoryModel Logical GLSL450 %2 = OpTypeFloat 16)"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, int16_bad) { std::string str = header + "%2 = OpTypeInt 16 1"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); 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()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, int64_bad) { std::string str = header + "%2 = OpTypeInt 64 1"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); 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"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_num_bits_error)); } TEST_F(ValidateData, float16_good) { std::string str = header_with_float16 + "%2 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, float16_buffer_good) { std::string str = header_with_float16_buffer + "%2 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, float16_bad) { std::string str = header + "%2 = OpTypeFloat 16"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); 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()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, float64_bad) { std::string str = header + "%2 = OpTypeFloat 64"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); 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"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_num_bits_error)); } TEST_F(ValidateData, matrix_data_type_float) { std::string str = header + R"( %f32 = OpTypeFloat 32 %vec3 = OpTypeVector %f32 3 %mat33 = OpTypeMatrix %vec3 3 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, ids_should_be_validated_before_data) { std::string str = header + R"( %f32 = OpTypeFloat 32 %mat33 = OpTypeMatrix %vec3 3 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 3[%3] has not been defined")); } TEST_F(ValidateData, matrix_bad_column_type) { std::string str = header + R"( %f32 = OpTypeFloat 32 %mat33 = OpTypeMatrix %f32 3 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Columns in a matrix must be of type vector")); } TEST_F(ValidateData, matrix_data_type_int) { std::string str = header + R"( %int32 = OpTypeInt 32 1 %vec3 = OpTypeVector %int32 3 %mat33 = OpTypeMatrix %vec3 3 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("can only be parameterized with floating-point types")); } TEST_F(ValidateData, matrix_data_type_bool) { std::string str = header + R"( %boolt = OpTypeBool %vec3 = OpTypeVector %boolt 3 %mat33 = OpTypeMatrix %vec3 3 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("can only be parameterized with floating-point types")); } TEST_F(ValidateData, matrix_with_0_columns) { std::string str = header + R"( %f32 = OpTypeFloat 32 %vec3 = OpTypeVector %f32 3 %mat33 = OpTypeMatrix %vec3 0 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("can only be parameterized as having only 2, 3, or 4 columns")); } TEST_F(ValidateData, matrix_with_1_column) { std::string str = header + R"( %f32 = OpTypeFloat 32 %vec3 = OpTypeVector %f32 3 %mat33 = OpTypeMatrix %vec3 1 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("can only be parameterized as having only 2, 3, or 4 columns")); } TEST_F(ValidateData, matrix_with_2_columns) { std::string str = header + R"( %f32 = OpTypeFloat 32 %vec3 = OpTypeVector %f32 3 %mat33 = OpTypeMatrix %vec3 2 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, matrix_with_3_columns) { std::string str = header + R"( %f32 = OpTypeFloat 32 %vec3 = OpTypeVector %f32 3 %mat33 = OpTypeMatrix %vec3 3 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, matrix_with_4_columns) { std::string str = header + R"( %f32 = OpTypeFloat 32 %vec3 = OpTypeVector %f32 3 %mat33 = OpTypeMatrix %vec3 4 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, matrix_with_5_column) { std::string str = header + R"( %f32 = OpTypeFloat 32 %vec3 = OpTypeVector %f32 3 %mat33 = OpTypeMatrix %vec3 5 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("can only be parameterized as having only 2, 3, or 4 columns")); } TEST_F(ValidateData, specialize_int) { std::string str = header + R"( %i32 = OpTypeInt 32 1 %len = OpSpecConstant %i32 2)"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, specialize_float) { std::string str = header + R"( %f32 = OpTypeFloat 32 %len = OpSpecConstant %f32 2)"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, specialize_boolean) { std::string str = header + R"( %2 = OpTypeBool %3 = OpSpecConstantTrue %2 %4 = OpSpecConstantFalse %2)"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, specialize_boolean_to_int) { std::string str = header + R"( %2 = OpTypeInt 32 1 %3 = OpSpecConstantTrue %2 %4 = OpSpecConstantFalse %2)"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Specialization constant must be a boolean")); } TEST_F(ValidateData, missing_forward_pointer_decl) { std::string str = header_with_addresses + R"( %uintt = OpTypeInt 32 0 %3 = OpTypeStruct %fwd_ptrt %uintt )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("must first be declared using OpTypeForwardPointer")); } TEST_F(ValidateData, missing_forward_pointer_decl_self_reference) { std::string str = header_with_addresses + R"( %uintt = OpTypeInt 32 0 %3 = OpTypeStruct %3 %uintt )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("must first be declared using OpTypeForwardPointer")); } TEST_F(ValidateData, forward_pointer_missing_definition) { std::string str = header_with_addresses + R"( OpTypeForwardPointer %_ptr_Generic_struct_A Generic %uintt = OpTypeInt 32 0 %struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("forward referenced IDs have not been defined")); } TEST_F(ValidateData, forward_ref_bad_type) { std::string str = header_with_addresses + R"( OpTypeForwardPointer %_ptr_Generic_struct_A Generic %uintt = OpTypeInt 32 0 %struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A %_ptr_Generic_struct_A = OpTypeFloat 32 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Pointer type in OpTypeForwardPointer is not a pointer " "type.\n OpTypeForwardPointer %float Generic\n")); } TEST_F(ValidateData, forward_ref_points_to_non_struct) { std::string str = header_with_addresses + R"( OpTypeForwardPointer %_ptr_Generic_struct_A Generic %uintt = OpTypeInt 32 0 %struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A %_ptr_Generic_struct_A = OpTypePointer Generic %uintt )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("A forward reference operand in an OpTypeStruct must " "be an OpTypePointer that points to an OpTypeStruct. " "Found OpTypePointer that points to OpTypeInt.")); } TEST_F(ValidateData, struct_forward_pointer_good) { std::string str = header_with_addresses + R"( OpTypeForwardPointer %_ptr_Generic_struct_A Generic %uintt = OpTypeInt 32 0 %struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A %struct_C = OpTypeStruct %uintt %struct_B %struct_A = OpTypeStruct %uintt %struct_C %_ptr_Generic_struct_A = OpTypePointer Generic %struct_C )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateData, ext_16bit_storage_caps_allow_free_fp_rounding_mode) { for (const char* cap : {"StorageUniform16", "StorageUniformBufferBlock16", "StoragePushConstant16", "StorageInputOutput16"}) { for (const char* mode : {"RTE", "RTZ", "RTP", "RTN"}) { std::string str = std::string(R"( OpCapability Shader OpCapability Linkage OpCapability )") + cap + R"( OpExtension "SPV_KHR_storage_buffer_storage_class" OpExtension "SPV_KHR_variable_pointers" OpExtension "SPV_KHR_16bit_storage" OpMemoryModel Logical GLSL450 OpDecorate %_ FPRoundingMode )" + mode + R"( %half = OpTypeFloat 16 %float = OpTypeFloat 32 %float_1_25 = OpConstant %float 1.25 %half_ptr = OpTypePointer StorageBuffer %half %half_ptr_var = OpVariable %half_ptr StorageBuffer %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func %main_entry = OpLabel %_ = OpFConvert %half %float_1_25 OpStore %half_ptr_var %_ OpReturn OpFunctionEnd )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } } } TEST_F(ValidateData, vulkan_disallow_free_fp_rounding_mode) { for (const char* mode : {"RTE", "RTZ"}) { for (const auto env : {SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) { std::string str = std::string(R"( OpCapability Shader OpExtension "SPV_KHR_storage_buffer_storage_class" OpExtension "SPV_KHR_variable_pointers" OpMemoryModel Logical GLSL450 OpDecorate %_ FPRoundingMode )") + mode + R"( %half = OpTypeFloat 16 %float = OpTypeFloat 32 %float_1_25 = OpConstant %float 1.25 %half_ptr = OpTypePointer StorageBuffer %half %half_ptr_var = OpVariable %half_ptr StorageBuffer %void = OpTypeVoid %func = OpTypeFunction %void %main = OpFunction %void None %func %main_entry = OpLabel %_ = OpFConvert %half %float_1_25 OpStore %half_ptr_var %_ OpReturn OpFunctionEnd )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Operand 2 of Decorate requires one of these capabilities: " "StorageBuffer16BitAccess StorageUniform16 " "StoragePushConstant16 StorageInputOutput16")); } } } TEST_F(ValidateData, void_array) { std::string str = header + R"( %void = OpTypeVoid %int = OpTypeInt 32 0 %int_5 = OpConstant %int 5 %array = OpTypeArray %void %int_5 )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpTypeArray Element Type <id> '1[%void]' is a void type.")); } TEST_F(ValidateData, void_runtime_array) { std::string str = header + R"( %void = OpTypeVoid %array = OpTypeRuntimeArray %void )"; CompileSuccessfully(str.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( "OpTypeRuntimeArray Element Type <id> '1[%void]' is a void type.")); } TEST_F(ValidateData, vulkan_RTA_array_at_end_of_struct) { std::string str = R"( OpCapability Shader OpMemoryModel Logical GLSL450 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 )"; CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) { std::string str = R"( OpCapability Shader OpMemoryModel Logical GLSL450 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 )"; CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), HasSubstr("In Vulkan, OpTypeRuntimeArray must only be used for " "the last member of an OpTypeStruct\n %_struct_3 = " "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 )"; CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); } 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 )"; CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); 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")); } } // namespace } // namespace val } // namespace spvtools