diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index c47a74603..fb9d25327 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -389,6 +389,34 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } + // WebGPU & 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 (spvIsVulkanEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, '" << _.getIdName(inst->id()) + << "', has a disallowed initializer & storage class " + << "combination.\n" + << "From Vulkan spec, Appendix A:\n" + << "Variable declarations that include initializers must have " + << "one of the following storage classes: Output, Private, or " + << "Function"; + } + + if (spvIsWebGPUEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, '" << _.getIdName(inst->id()) + << "', has a disallowed initializer & storage class " + << "combination.\n" + << "From WebGPU execution environment spec:\n" + << "Variable declarations that include initializers must have " + << "one of the following storage classes: Output, Private, or " + << "Function"; + } + } + return SPV_SUCCESS; } diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 8b063754a..8dafa0d64 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -349,6 +349,200 @@ 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, '5', has a disallowed initializer & storage class " + "combination.\n" + "From WebGPU execution environment spec:\n" + "Variable 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, VulkanInitializerWithOutputStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +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_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanInitializerWithFunctionStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +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_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanInitializerWithPrivateStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +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_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanInitializerWithDisallowedStorageClassesBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Input %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Input %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +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(), + HasSubstr( + "OpVariable, '5', has a disallowed initializer & storage class " + "combination.\n" + "From Vulkan spec, Appendix A:\n" + "Variable declarations that include initializers must have one of " + "the following storage classes: Output, Private, or Function\n " + "%5 = OpVariable %_ptr_Input_float Input %float_1\n")); +} + } // namespace } // namespace val } // namespace spvtools