// 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. #include #include #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::Not; using ValidateBarriers = spvtest::ValidateBase; std::string GenerateShaderCodeImpl( const std::string& body, const std::string& capabilities_and_extensions, const std::string& definitions, const std::string& execution_model, const std::string& memory_model) { std::ostringstream ss; ss << R"( OpCapability Shader )"; ss << capabilities_and_extensions; ss << memory_model << std::endl; ss << "OpEntryPoint " << execution_model << " %main \"main\"\n"; if (execution_model == "Fragment") { ss << "OpExecutionMode %main OriginUpperLeft\n"; } else if (execution_model == "Geometry") { ss << "OpExecutionMode %main InputPoints\n"; ss << "OpExecutionMode %main OutputPoints\n"; } else if (execution_model == "GLCompute") { ss << "OpExecutionMode %main LocalSize 1 1 1\n"; } ss << R"( %void = OpTypeVoid %func = OpTypeFunction %void %bool = OpTypeBool %f32 = OpTypeFloat 32 %u32 = OpTypeInt 32 0 %f32_0 = OpConstant %f32 0 %f32_1 = OpConstant %f32 1 %u32_0 = OpConstant %u32 0 %u32_1 = OpConstant %u32 1 %u32_4 = OpConstant %u32 4 )"; ss << definitions; ss << R"( %cross_device = OpConstant %u32 0 %device = OpConstant %u32 1 %workgroup = OpConstant %u32 2 %subgroup = OpConstant %u32 3 %invocation = OpConstant %u32 4 %queuefamily = OpConstant %u32 5 %shadercall = OpConstant %u32 6 %none = OpConstant %u32 0 %acquire = OpConstant %u32 2 %release = OpConstant %u32 4 %acquire_release = OpConstant %u32 8 %acquire_and_release = OpConstant %u32 6 %sequentially_consistent = OpConstant %u32 16 %acquire_release_uniform_workgroup = OpConstant %u32 328 %acquire_uniform_workgroup = OpConstant %u32 322 %release_uniform_workgroup = OpConstant %u32 324 %acquire_and_release_uniform = OpConstant %u32 70 %acquire_release_subgroup = OpConstant %u32 136 %acquire_release_workgroup = OpConstant %u32 264 %uniform = OpConstant %u32 64 %uniform_workgroup = OpConstant %u32 320 %workgroup_memory = OpConstant %u32 256 %image_memory = OpConstant %u32 2048 %uniform_image_memory = OpConstant %u32 2112 %main = OpFunction %void None %func %main_entry = OpLabel )"; ss << body; ss << R"( OpReturn OpFunctionEnd)"; return ss.str(); } std::string GenerateShaderCode( const std::string& body, const std::string& capabilities_and_extensions = "", const std::string& execution_model = "GLCompute") { const std::string int64_capability = R"( OpCapability Int64 )"; const std::string int64_declarations = R"( %u64 = OpTypeInt 64 0 %u64_0 = OpConstant %u64 0 %u64_1 = OpConstant %u64 1 )"; const std::string memory_model = "OpMemoryModel Logical GLSL450"; return GenerateShaderCodeImpl( body, int64_capability + capabilities_and_extensions, int64_declarations, execution_model, memory_model); } std::string GenerateVulkanVertexShaderCode( const std::string& body, const std::string& capabilities_and_extensions = "", const std::string& execution_model = "Vertex") { const std::string memory_model = "OpMemoryModel Logical GLSL450"; return GenerateShaderCodeImpl(body, capabilities_and_extensions, "", execution_model, memory_model); } std::string GenerateKernelCode( const std::string& body, const std::string& capabilities_and_extensions = "") { std::ostringstream ss; ss << R"( OpCapability Addresses OpCapability Kernel OpCapability Linkage OpCapability Int64 OpCapability NamedBarrier )"; ss << capabilities_and_extensions; ss << R"( OpMemoryModel Physical32 OpenCL %void = OpTypeVoid %func = OpTypeFunction %void %bool = OpTypeBool %f32 = OpTypeFloat 32 %u32 = OpTypeInt 32 0 %u64 = OpTypeInt 64 0 %f32_0 = OpConstant %f32 0 %f32_1 = OpConstant %f32 1 %f32_4 = OpConstant %f32 4 %u32_0 = OpConstant %u32 0 %u32_1 = OpConstant %u32 1 %u32_4 = OpConstant %u32 4 %u64_0 = OpConstant %u64 0 %u64_1 = OpConstant %u64 1 %u64_4 = OpConstant %u64 4 %cross_device = OpConstant %u32 0 %device = OpConstant %u32 1 %workgroup = OpConstant %u32 2 %subgroup = OpConstant %u32 3 %invocation = OpConstant %u32 4 %none = OpConstant %u32 0 %acquire = OpConstant %u32 2 %release = OpConstant %u32 4 %acquire_release = OpConstant %u32 8 %acquire_and_release = OpConstant %u32 6 %sequentially_consistent = OpConstant %u32 16 %acquire_release_workgroup = OpConstant %u32 264 %named_barrier = OpTypeNamedBarrier %main = OpFunction %void None %func %main_entry = OpLabel )"; ss << body; ss << R"( OpReturn OpFunctionEnd)"; return ss.str(); } TEST_F(ValidateBarriers, OpControlBarrierGLComputeSuccess) { const std::string body = R"( OpControlBarrier %device %device %none OpControlBarrier %workgroup %workgroup %acquire OpControlBarrier %workgroup %device %release OpControlBarrier %cross_device %cross_device %acquire_release OpControlBarrier %cross_device %cross_device %sequentially_consistent OpControlBarrier %cross_device %cross_device %acquire_release_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateBarriers, OpControlBarrierKernelSuccess) { const std::string body = R"( OpControlBarrier %device %device %none OpControlBarrier %workgroup %workgroup %acquire OpControlBarrier %workgroup %device %release OpControlBarrier %cross_device %cross_device %acquire_release OpControlBarrier %cross_device %cross_device %sequentially_consistent OpControlBarrier %cross_device %cross_device %acquire_release_workgroup )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierTesselationControlSuccess) { const std::string body = R"( OpControlBarrier %device %device %none OpControlBarrier %workgroup %workgroup %acquire OpControlBarrier %workgroup %device %release OpControlBarrier %cross_device %cross_device %acquire_release OpControlBarrier %cross_device %cross_device %sequentially_consistent OpControlBarrier %cross_device %cross_device %acquire_release_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", "TessellationControl")); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateBarriers, OpControlBarrierVulkanSuccess) { const std::string body = R"( OpControlBarrier %workgroup %device %none OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv12) { const std::string body = R"( OpControlBarrier %device %device %none )"; CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), SPV_ENV_UNIVERSAL_1_2); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpControlBarrier requires one of the following " "Execution Models: TessellationControl, GLCompute, Kernel, " "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv13) { const std::string body = R"( OpControlBarrier %device %device %none )"; CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), SPV_ENV_UNIVERSAL_1_3); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateBarriers, OpControlBarrierFloatExecutionScope) { const std::string body = R"( OpControlBarrier %f32_1 %device %none )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: expected scope to be a 32-bit int")); } TEST_F(ValidateBarriers, OpControlBarrierU64ExecutionScope) { const std::string body = R"( OpControlBarrier %u64_1 %device %none )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: expected scope to be a 32-bit int")); } TEST_F(ValidateBarriers, OpControlBarrierFloatMemoryScope) { const std::string body = R"( OpControlBarrier %device %f32_1 %none )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: expected scope to be a 32-bit int")); } TEST_F(ValidateBarriers, OpControlBarrierU64MemoryScope) { const std::string body = R"( OpControlBarrier %device %u64_1 %none )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: expected scope to be a 32-bit int")); } TEST_F(ValidateBarriers, OpControlBarrierFloatMemorySemantics) { const std::string body = R"( OpControlBarrier %device %device %f32_0 )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( "ControlBarrier: expected Memory Semantics to be a 32-bit int")); } TEST_F(ValidateBarriers, OpControlBarrierU64MemorySemantics) { const std::string body = R"( OpControlBarrier %device %device %u64_0 )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr( "ControlBarrier: expected Memory Semantics to be a 32-bit int")); } TEST_F(ValidateBarriers, OpControlBarrierVulkanExecutionScopeDevice) { const std::string body = R"( OpControlBarrier %device %workgroup %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-04636")); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: in Vulkan environment Execution Scope " "is limited to Workgroup and Subgroup")); } TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroup) { const std::string body = R"( 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-SubgroupVoteKHR-07951")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "ControlBarrier: in Vulkan 1.0 environment Memory Scope is can not " "be Subgroup without SubgroupBallotKHR or SubgroupVoteKHR declared")); } TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroupVoteKHR) { const std::string capabilities = R"( OpCapability SubgroupVoteKHR OpExtension "SPV_KHR_subgroup_vote" )"; const std::string body = R"( OpControlBarrier %subgroup %subgroup %none )"; CompileSuccessfully(GenerateShaderCode(body, capabilities), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeSubgroup) { const std::string body = R"( OpControlBarrier %subgroup %subgroup %none )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeCrossDevice) { const std::string body = R"( 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 is " "limited to Device, QueueFamily, Workgroup, " "ShaderCallKHR, Subgroup, or Invocation")); } TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeMemoryFailure) { const std::string body = R"( OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-07321")); EXPECT_THAT( getDiagnosticString(), HasSubstr("Workgroup Memory Scope is limited to MeshNV, " "TaskNV, MeshEXT, TaskEXT, TessellationControl, and GLCompute " "execution model")); } TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeExecutionFailure) { const std::string body = R"( OpControlBarrier %workgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-04637")); EXPECT_THAT(getDiagnosticString(), HasSubstr("in Vulkan environment, Workgroup execution scope is " "only for TaskNV, MeshNV, TaskEXT, MeshEXT, " "TessellationControl, and GLCompute execution models")); } TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupComputeSuccess) { const std::string body = R"( OpControlBarrier %workgroup %workgroup %acquire_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) { const std::string body = R"( OpControlBarrier %subgroup %subgroup %acquire_uniform_workgroup )"; CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierVulkanInvocationSuccess) { const std::string body = R"( OpControlBarrier %workgroup %invocation %none )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } TEST_F(ValidateBarriers, OpControlBarrierVulkanInvocationFailure) { const std::string body = R"( OpControlBarrier %workgroup %invocation %acquire )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-04641")); EXPECT_THAT( getDiagnosticString(), HasSubstr("ControlBarrier: Vulkan specification requires Memory " "Semantics to be None if used with Invocation Memory Scope")); } TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) { const std::string body = R"( OpControlBarrier %device %device %acquire_and_release_uniform )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: Memory Semantics can have at most one " "of the following bits set: Acquire, Release, " "AcquireRelease or SequentiallyConsistent")); } TEST_F(ValidateBarriers, OpControlBarrierVulkanSubgroupStorageClass) { const std::string body = R"( OpControlBarrier %workgroup %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-OpControlBarrier-04650")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "ControlBarrier: expected Memory Semantics to include a " "Vulkan-supported storage class if Memory Semantics is not None")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p1) { const std::string body = R"( OpControlBarrier %subgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionFragment1p1) { const std::string body = R"( OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "OpControlBarrier execution scope must be Subgroup for Fragment, " "Vertex, Geometry, TessellationEvaluation, RayGeneration, " "Intersection, AnyHit, ClosestHit, and Miss execution models")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p0) { const std::string body = R"( OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpControlBarrier requires one of the following " "Execution " "Models: TessellationControl, GLCompute, Kernel, " "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p1) { const std::string body = R"( OpControlBarrier %subgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionVertex1p1) { const std::string body = R"( OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "OpControlBarrier execution scope must be Subgroup for Fragment, " "Vertex, Geometry, TessellationEvaluation, RayGeneration, " "Intersection, AnyHit, ClosestHit, and Miss execution models")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p0) { const std::string body = R"( OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpControlBarrier requires one of the following " "Execution Models: TessellationControl, GLCompute, Kernel, " "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p1) { const std::string body = R"( OpControlBarrier %subgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully( GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionGeometry1p1) { const std::string body = R"( OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully( GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "OpControlBarrier execution scope must be Subgroup for Fragment, " "Vertex, Geometry, TessellationEvaluation, RayGeneration, " "Intersection, AnyHit, ClosestHit, and Miss execution models")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p0) { const std::string body = R"( OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully( GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpControlBarrier requires one of the following " "Execution " "Models: TessellationControl, GLCompute, Kernel, " "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionTessellationEvaluation1p1) { const std::string body = R"( OpControlBarrier %subgroup %subgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", "TessellationEvaluation"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionTessellationEvaluation1p1) { const std::string body = R"( OpControlBarrier %workgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", "TessellationEvaluation"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "OpControlBarrier execution scope must be Subgroup for Fragment, " "Vertex, Geometry, TessellationEvaluation, RayGeneration, " "Intersection, AnyHit, ClosestHit, and Miss execution models")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionTessellationEvaluation1p0) { const std::string body = R"( OpControlBarrier %subgroup %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", "TessellationEvaluation"), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), HasSubstr("OpControlBarrier requires one of the following " "Execution " "Models: TessellationControl, GLCompute, Kernel, " "MeshNV or TaskNV")); } TEST_F(ValidateBarriers, OpMemoryBarrierSuccess) { const std::string body = R"( OpMemoryBarrier %cross_device %acquire_release_uniform_workgroup OpMemoryBarrier %device %uniform )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateBarriers, OpMemoryBarrierKernelSuccess) { const std::string body = R"( OpMemoryBarrier %cross_device %acquire_release_workgroup OpMemoryBarrier %device %none )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); } TEST_F(ValidateBarriers, OpMemoryBarrierVulkanSuccess) { const std::string body = R"( OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) { const std::string body = R"( OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryBarrier: expected scope to be a 32-bit int")); } TEST_F(ValidateBarriers, OpMemoryBarrierU64MemoryScope) { const std::string body = R"( OpMemoryBarrier %u64_1 %acquire_release_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryBarrier: expected scope to be a 32-bit int")); } TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemorySemantics) { const std::string body = R"( OpMemoryBarrier %device %f32_0 )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("MemoryBarrier: expected Memory Semantics to be a 32-bit int")); } TEST_F(ValidateBarriers, OpMemoryBarrierU64MemorySemantics) { const std::string body = R"( OpMemoryBarrier %device %u64_0 )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("MemoryBarrier: expected Memory Semantics to be a 32-bit int")); } TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemoryScopeSubgroup) { const std::string body = R"( 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-SubgroupVoteKHR-07951")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "MemoryBarrier: in Vulkan 1.0 environment Memory Scope is can not be " "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR declared")); } TEST_F(ValidateBarriers, OpMemoryBarrierVulkan1p1MemoryScopeSubgroup) { const std::string body = R"( OpMemoryBarrier %subgroup %acquire_release_uniform_workgroup )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpMemoryBarrierAcquireAndRelease) { const std::string body = R"( OpMemoryBarrier %device %acquire_and_release_uniform )"; CompileSuccessfully(GenerateShaderCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryBarrier: Memory Semantics can have at most one " "of the following bits set: Acquire, Release, " "AcquireRelease or SequentiallyConsistent")); } TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsNone) { const std::string body = R"( 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 " "to have one of the following bits set: Acquire, Release, " "AcquireRelease or SequentiallyConsistent")); } TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsAcquire) { const std::string body = R"( 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")); } TEST_F(ValidateBarriers, OpMemoryBarrierVulkanSubgroupStorageClass) { const std::string body = R"( 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")); } TEST_F(ValidateBarriers, OpNamedBarrierInitializeSuccess) { const std::string body = R"( %barrier = OpNamedBarrierInitialize %named_barrier %u32_4 )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); } TEST_F(ValidateBarriers, OpNamedBarrierInitializeWrongResultType) { const std::string body = R"( %barrier = OpNamedBarrierInitialize %u32 %u32_4 )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT(getDiagnosticString(), HasSubstr("NamedBarrierInitialize: expected Result Type to be " "OpTypeNamedBarrier")); } TEST_F(ValidateBarriers, OpNamedBarrierInitializeFloatSubgroupCount) { const std::string body = R"( %barrier = OpNamedBarrierInitialize %named_barrier %f32_4 )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT(getDiagnosticString(), HasSubstr("NamedBarrierInitialize: expected Subgroup Count to be " "a 32-bit int")); } TEST_F(ValidateBarriers, OpNamedBarrierInitializeU64SubgroupCount) { const std::string body = R"( %barrier = OpNamedBarrierInitialize %named_barrier %u64_4 )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT(getDiagnosticString(), HasSubstr("NamedBarrierInitialize: expected Subgroup Count to be " "a 32-bit int")); } TEST_F(ValidateBarriers, OpMemoryNamedBarrierSuccess) { const std::string body = R"( %barrier = OpNamedBarrierInitialize %named_barrier %u32_4 OpMemoryNamedBarrier %barrier %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); } TEST_F(ValidateBarriers, OpMemoryNamedBarrierNotNamedBarrier) { const std::string body = R"( OpMemoryNamedBarrier %u32_1 %workgroup %acquire_release_workgroup )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryNamedBarrier: expected Named Barrier to be of " "type OpTypeNamedBarrier")); } TEST_F(ValidateBarriers, OpMemoryNamedBarrierFloatMemoryScope) { const std::string body = R"( %barrier = OpNamedBarrierInitialize %named_barrier %u32_4 OpMemoryNamedBarrier %barrier %f32_1 %acquire_release_workgroup )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT( getDiagnosticString(), HasSubstr("MemoryNamedBarrier: expected scope to be a 32-bit int")); } TEST_F(ValidateBarriers, OpMemoryNamedBarrierFloatMemorySemantics) { const std::string body = R"( %barrier = OpNamedBarrierInitialize %named_barrier %u32_4 OpMemoryNamedBarrier %barrier %workgroup %f32_0 )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT( getDiagnosticString(), HasSubstr( "MemoryNamedBarrier: expected Memory Semantics to be a 32-bit int")); } TEST_F(ValidateBarriers, OpMemoryNamedBarrierAcquireAndRelease) { const std::string body = R"( %barrier = OpNamedBarrierInitialize %named_barrier %u32_4 OpMemoryNamedBarrier %barrier %workgroup %acquire_and_release )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryNamedBarrier: Memory Semantics can have at most " "one of the following bits set: Acquire, Release, " "AcquireRelease or SequentiallyConsistent")); } TEST_F(ValidateBarriers, TypeAsMemoryScope) { const std::string body = R"( OpMemoryBarrier %u32 %u32_0 )"; CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '5[%uint]' cannot be a " "type")); } TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryModelBanSequentiallyConsistent) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR OpEntryPoint Fragment %1 "func" OpExecutionMode %1 OriginUpperLeft %2 = OpTypeVoid %3 = OpTypeInt 32 0 %4 = OpConstant %3 16 %5 = OpTypeFunction %2 %6 = OpConstant %3 5 %1 = OpFunction %2 None %5 %7 = OpLabel OpControlBarrier %6 %6 %4 OpReturn OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("SequentiallyConsistent memory semantics cannot be " "used with the VulkanKHR memory model.")); } TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemoryModelBanSequentiallyConsistent) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR OpEntryPoint Fragment %1 "func" OpExecutionMode %1 OriginUpperLeft %2 = OpTypeVoid %3 = OpTypeInt 32 0 %4 = OpConstant %3 16 %5 = OpTypeFunction %2 %6 = OpConstant %3 5 %1 = OpFunction %2 None %5 %7 = OpLabel OpMemoryBarrier %6 %4 OpReturn OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("SequentiallyConsistent memory semantics cannot be " "used with the VulkanKHR memory model.")); } TEST_F(ValidateBarriers, OutputMemoryKHRRequireVulkanMemoryModelKHR) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "func" OpExecutionMode %1 OriginUpperLeft %2 = OpTypeVoid %3 = OpTypeInt 32 0 %semantics = OpConstant %3 4104 %5 = OpTypeFunction %2 %device = OpConstant %3 1 %1 = OpFunction %2 None %5 %7 = OpLabel OpControlBarrier %device %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: Memory Semantics OutputMemoryKHR " "requires capability VulkanMemoryModelKHR")); } TEST_F(ValidateBarriers, MakeAvailableKHRRequireVulkanMemoryModelKHR) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "func" OpExecutionMode %1 OriginUpperLeft %2 = OpTypeVoid %3 = OpTypeInt 32 0 %semantics = OpConstant %3 8264 %5 = OpTypeFunction %2 %device = OpConstant %3 1 %1 = OpFunction %2 None %5 %7 = OpLabel OpControlBarrier %device %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: Memory Semantics MakeAvailableKHR " "requires capability VulkanMemoryModelKHR")); } TEST_F(ValidateBarriers, MakeVisibleKHRRequireVulkanMemoryModelKHR) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %1 "func" OpExecutionMode %1 OriginUpperLeft %2 = OpTypeVoid %3 = OpTypeInt 32 0 %semantics = OpConstant %3 16456 %5 = OpTypeFunction %2 %device = OpConstant %3 1 %1 = OpFunction %2 None %5 %7 = OpLabel OpControlBarrier %device %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: Memory Semantics MakeVisibleKHR " "requires capability VulkanMemoryModelKHR")); } TEST_F(ValidateBarriers, MakeAvailableKHRRequiresReleaseSemantics) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft %void = OpTypeVoid %int = OpTypeInt 32 0 %workgroup = OpConstant %int 2 %semantics = OpConstant %int 8448 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpControlBarrier %workgroup %workgroup %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("ControlBarrier: MakeAvailableKHR Memory Semantics also " "requires either Release or AcquireRelease Memory Semantics")); } TEST_F(ValidateBarriers, MakeVisibleKHRRequiresAcquireSemantics) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft %void = OpTypeVoid %int = OpTypeInt 32 0 %workgroup = OpConstant %int 2 %semantics = OpConstant %int 16640 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpControlBarrier %workgroup %workgroup %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("ControlBarrier: MakeVisibleKHR Memory Semantics also requires " "either Acquire or AcquireRelease Memory Semantics")); } TEST_F(ValidateBarriers, MakeAvailableKHRRequiresStorageSemantics) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft %void = OpTypeVoid %int = OpTypeInt 32 0 %workgroup = OpConstant %int 2 %semantics = OpConstant %int 8196 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpMemoryBarrier %workgroup %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryBarrier: expected Memory Semantics to include a " "storage class")); } TEST_F(ValidateBarriers, MakeVisibleKHRRequiresStorageSemantics) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft %void = OpTypeVoid %int = OpTypeInt 32 0 %workgroup = OpConstant %int 2 %semantics = OpConstant %int 16386 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpMemoryBarrier %workgroup %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT(getDiagnosticString(), HasSubstr("MemoryBarrier: expected Memory Semantics to include a " "storage class")); } TEST_F(ValidateBarriers, SemanticsSpecConstantShader) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft %void = OpTypeVoid %int = OpTypeInt 32 0 %ptr_int_workgroup = OpTypePointer Workgroup %int %var = OpVariable %ptr_int_workgroup Workgroup %voidfn = OpTypeFunction %void %spec_const = OpSpecConstant %int 0 %workgroup = OpConstant %int 2 %func = OpFunction %void None %voidfn %entry = OpLabel OpMemoryBarrier %workgroup %spec_const OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Memory Semantics ids must be OpConstant when Shader " "capability is present")); } TEST_F(ValidateBarriers, SemanticsSpecConstantKernel) { const std::string spirv = R"( OpCapability Kernel OpCapability Linkage OpMemoryModel Logical OpenCL %void = OpTypeVoid %int = OpTypeInt 32 0 %ptr_int_workgroup = OpTypePointer Workgroup %int %var = OpVariable %ptr_int_workgroup Workgroup %voidfn = OpTypeFunction %void %spec_const = OpSpecConstant %int 0 %workgroup = OpConstant %int 2 %func = OpFunction %void None %voidfn %entry = OpLabel OpMemoryBarrier %workgroup %spec_const OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateBarriers, ScopeSpecConstantShader) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft %void = OpTypeVoid %int = OpTypeInt 32 0 %ptr_int_workgroup = OpTypePointer Workgroup %int %var = OpVariable %ptr_int_workgroup Workgroup %voidfn = OpTypeFunction %void %spec_const = OpSpecConstant %int 0 %relaxed = OpConstant %int 0 %func = OpFunction %void None %voidfn %entry = OpLabel OpMemoryBarrier %spec_const %relaxed OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Scope ids must be OpConstant when Shader " "capability is present")); } TEST_F(ValidateBarriers, ScopeSpecConstantKernel) { const std::string spirv = R"( OpCapability Kernel OpCapability Linkage OpMemoryModel Logical OpenCL %void = OpTypeVoid %int = OpTypeInt 32 0 %ptr_int_workgroup = OpTypePointer Workgroup %int %var = OpVariable %ptr_int_workgroup Workgroup %voidfn = OpTypeFunction %void %spec_const = OpSpecConstant %int 0 %relaxed = OpConstant %int 0 %func = OpFunction %void None %voidfn %entry = OpLabel OpMemoryBarrier %spec_const %relaxed OpReturn OpFunctionEnd )"; CompileSuccessfully(spirv); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateBarriers, VulkanMemoryModelDeviceScopeBad) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft %void = OpTypeVoid %int = OpTypeInt 32 0 %device = OpConstant %int 1 %semantics = OpConstant %int 0 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpMemoryBarrier %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); EXPECT_THAT( getDiagnosticString(), HasSubstr("Use of device scope with VulkanKHR memory model requires the " "VulkanMemoryModelDeviceScopeKHR capability")); } TEST_F(ValidateBarriers, VulkanMemoryModelDeviceScopeGood) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpCapability VulkanMemoryModelDeviceScopeKHR OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR OpEntryPoint Fragment %func "func" OpExecutionMode %func OriginUpperLeft %void = OpTypeVoid %int = OpTypeInt 32 0 %device = OpConstant %int 1 %semantics = OpConstant %int 0 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpMemoryBarrier %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } TEST_F(ValidateBarriers, VolatileMemoryBarrier) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpCapability VulkanMemoryModelDeviceScopeKHR OpCapability Linkage OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR %void = OpTypeVoid %int = OpTypeInt 32 0 %device = OpConstant %int 1 %semantics = OpConstant %int 32768 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpMemoryBarrier %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Memory Semantics Volatile can only be used with " "atomic instructions")); } TEST_F(ValidateBarriers, VolatileControlBarrier) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpCapability VulkanMemoryModelDeviceScopeKHR OpCapability Linkage OpExtension "SPV_KHR_vulkan_memory_model" OpMemoryModel Logical VulkanKHR %void = OpTypeVoid %int = OpTypeInt 32 0 %device = OpConstant %int 1 %semantics = OpConstant %int 32768 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpControlBarrier %device %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Memory Semantics Volatile can only be used with " "atomic instructions")); } TEST_F(ValidateBarriers, CooperativeMatrixSpecConstantVolatile) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpCapability VulkanMemoryModelDeviceScopeKHR OpCapability CooperativeMatrixNV OpCapability Linkage OpExtension "SPV_KHR_vulkan_memory_model" OpExtension "SPV_NV_cooperative_matrix" OpMemoryModel Logical VulkanKHR %void = OpTypeVoid %int = OpTypeInt 32 0 %device = OpConstant %int 1 %semantics = OpSpecConstant %int 32768 %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpControlBarrier %device %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateBarriers, CooperativeMatrixNonConstantSemantics) { const std::string text = R"( OpCapability Shader OpCapability VulkanMemoryModelKHR OpCapability VulkanMemoryModelDeviceScopeKHR OpCapability CooperativeMatrixNV OpCapability Linkage OpExtension "SPV_KHR_vulkan_memory_model" OpExtension "SPV_NV_cooperative_matrix" OpMemoryModel Logical VulkanKHR %void = OpTypeVoid %int = OpTypeInt 32 0 %device = OpConstant %int 1 %semantics = OpUndef %int %functy = OpTypeFunction %void %func = OpFunction %void None %functy %1 = OpLabel OpControlBarrier %device %device %semantics OpReturn OpFunctionEnd )"; CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Memory Semantics must be a constant instruction when " "CooperativeMatrixNV capability is present")); } TEST_F(ValidateBarriers, OpMemoryBarrierShaderCallRayGenSuccess) { const std::string body = "OpMemoryBarrier %shadercall %release_uniform_workgroup"; CompileSuccessfully(GenerateShaderCodeImpl(body, // capabilities_and_extensions R"( OpCapability VulkanMemoryModelKHR OpCapability RayTracingKHR OpExtension "SPV_KHR_vulkan_memory_model" OpExtension "SPV_KHR_ray_tracing" )", // definitions "", // execution_model "RayGenerationKHR", // memory_model "OpMemoryModel Logical VulkanKHR"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } TEST_F(ValidateBarriers, OpMemoryBarrierShaderCallComputeFailure) { const std::string body = "OpMemoryBarrier %shadercall %release_uniform_workgroup"; CompileSuccessfully(GenerateShaderCodeImpl(body, // capabilities_and_extensions R"( OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )", // definitions "", // execution_model "GLCompute", // memory_model "OpMemoryModel Logical VulkanKHR"), 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( "ShaderCallKHR Memory Scope requires a ray tracing execution model")); } TEST_F(ValidateBarriers, OpControlBarrierShaderCallRayGenFailure) { const std::string body = "OpControlBarrier %shadercall %shadercall %none"; CompileSuccessfully(GenerateShaderCodeImpl(body, // capabilities_and_extensions R"( OpCapability VulkanMemoryModelKHR OpCapability RayTracingKHR OpExtension "SPV_KHR_vulkan_memory_model" OpExtension "SPV_KHR_ray_tracing" )", // definitions "", // execution_model "RayGenerationKHR", // memory_model "OpMemoryModel Logical VulkanKHR"), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-StandaloneSpirv-None-04636")); EXPECT_THAT(getDiagnosticString(), HasSubstr("in Vulkan environment Execution Scope is limited to " "Workgroup and Subgroup")); } } // namespace } // namespace val } // namespace spvtools