| // 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, NonVulkanKHRMemoryModelBad) { | |
| std::string spirv = R"( | |
| OpCapability Shader | |
| OpMemoryModel Logical GLSL450 | |
| OpNoLine | |
| )"; | |
| CompileSuccessfully(spirv); | |
| EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); | |
| EXPECT_THAT(getDiagnosticString(), | |
| HasSubstr("Memory model must be VulkanKHR for WebGPU " | |
| "environment.\n OpMemoryModel Logical GLSL450\n")); | |
| } | |
| TEST_F(ValidateWebGPU, WhitelistedExtendedInstructionsImportGood) { | |
| 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, NonWhitelistedExtendedInstructionsImportBad) { | |
| 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 |