From 16a0da370b68897c566a5857c8db4c871a66f692 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Mon, 14 Jan 2019 13:52:28 -0500 Subject: [PATCH] Ensure that entry point names are unique for WebGPU (#2281) Fixes #2275 --- source/val/validate.cpp | 59 +++++++++++++++++++++++++- test/val/val_validation_state_test.cpp | 48 +++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/source/val/validate.cpp b/source/val/validate.cpp index 5d0c6243f..9797d31ad 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -169,6 +169,57 @@ spv_result_t ValidateForwardDecls(ValidationState_t& _) { << id_str.substr(0, id_str.size() - 1); } +std::vector CalculateNamesForEntryPoint(ValidationState_t& _, + const uint32_t id) { + auto id_descriptions = _.entry_point_descriptions(id); + auto id_names = std::vector(); + 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(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 @@ -177,7 +228,7 @@ spv_result_t ValidateForwardDecls(ValidationState_t& _) { // OpFunctionCall instruction. // // Additionally enforces that entry points for Vulkan and WebGPU should not have -// recursion. +// recursion. And that entry names should be unique for WebGPU. spv_result_t ValidateEntryPoints(ValidationState_t& _) { _.ComputeFunctionToEntryPointMapping(); _.ComputeRecursiveEntryPoints(); @@ -206,6 +257,12 @@ spv_result_t ValidateEntryPoints(ValidationState_t& _) { << "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; diff --git a/test/val/val_validation_state_test.cpp b/test/val/val_validation_state_test.cpp index e010fe9f2..bf6509497 100644 --- a/test/val/val_validation_state_test.cpp +++ b/test/val/val_validation_state_test.cpp @@ -306,6 +306,54 @@ TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) { "called.\n %9 = OpFunctionCall %_struct_5 %10\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