spirv-val: Add Vulkan Memory Scope VUs (#4106)


* Fix test using Fragment execution
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index 636d54c..a92f7fd 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -190,7 +190,7 @@
   if (spvIsVulkanEnv(_.context()->target_env)) {
     if (value == SpvScopeCrossDevice) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << spvOpcodeString(opcode)
+             << _.VkErrorID(4638) << spvOpcodeString(opcode)
              << ": in Vulkan environment, Memory Scope cannot be CrossDevice";
     }
     // Vulkan 1.0 specifc rules
@@ -198,7 +198,7 @@
         value != SpvScopeDevice && value != SpvScopeWorkgroup &&
         value != SpvScopeInvocation) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << spvOpcodeString(opcode)
+             << _.VkErrorID(4638) << spvOpcodeString(opcode)
              << ": in Vulkan 1.0 environment Memory Scope is limited to "
              << "Device, Workgroup and Invocation";
     }
@@ -209,15 +209,16 @@
         value != SpvScopeSubgroup && value != SpvScopeInvocation &&
         value != SpvScopeShaderCallKHR) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << spvOpcodeString(opcode)
+             << _.VkErrorID(4638) << spvOpcodeString(opcode)
              << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited "
              << "to Device, Workgroup, Invocation, and ShaderCall";
     }
 
     if (value == SpvScopeShaderCallKHR) {
+      std::string errorVUID = _.VkErrorID(4640);
       _.function(inst->function()->id())
           ->RegisterExecutionModelLimitation(
-              [](SpvExecutionModel model, std::string* message) {
+              [errorVUID](SpvExecutionModel model, std::string* message) {
                 if (model != SpvExecutionModelRayGenerationKHR &&
                     model != SpvExecutionModelIntersectionKHR &&
                     model != SpvExecutionModelAnyHitKHR &&
@@ -226,6 +227,7 @@
                     model != SpvExecutionModelCallableKHR) {
                   if (message) {
                     *message =
+                        errorVUID +
                         "ShaderCallKHR Memory Scope requires a ray tracing "
                         "execution model";
                   }
@@ -234,6 +236,25 @@
                 return true;
               });
     }
+
+    if (value == SpvScopeWorkgroup) {
+      std::string errorVUID = _.VkErrorID(4639);
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [errorVUID](SpvExecutionModel model, std::string* message) {
+                if (model != SpvExecutionModelGLCompute &&
+                    model != SpvExecutionModelTaskNV &&
+                    model != SpvExecutionModelMeshNV) {
+                  if (message) {
+                    *message = errorVUID +
+                               "Workgroup Memory Scope is limited to MeshNV, "
+                               "TaskNV, and GLCompute execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+    }
   }
 
   // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 367506e..92b51c7 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1652,6 +1652,12 @@
       return VUID_WRAP(VUID-StandaloneSpirv-None-04633);
     case 4635:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04635);
+    case 4638:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04638);
+    case 4639:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04639);
+    case 4640:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04640);
     case 4642:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04642);
     case 4649:
diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp
index 8a79e45..c7e36f5 100644
--- a/test/val/val_atomics_test.cpp
+++ b/test/val/val_atomics_test.cpp
@@ -30,16 +30,16 @@
 
 std::string GenerateShaderCodeImpl(
     const std::string& body, const std::string& capabilities_and_extensions,
-    const std::string& definitions, const std::string& memory_model) {
+    const std::string& definitions, const std::string& memory_model,
+    const std::string& execution) {
   std::ostringstream ss;
   ss << R"(
 OpCapability Shader
 )";
   ss << capabilities_and_extensions;
   ss << "OpMemoryModel Logical " << memory_model << "\n";
+  ss << execution;
   ss << R"(
-OpEntryPoint Fragment %main "main"
-OpExecutionMode %main OriginUpperLeft
 %void = OpTypeVoid
 %func = OpTypeFunction %void
 %bool = OpTypeBool
@@ -96,6 +96,10 @@
     const std::string& body,
     const std::string& capabilities_and_extensions = "",
     const std::string& memory_model = "GLSL450") {
+  const std::string execution = R"(
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+)";
   const std::string defintions = R"(
 %u64 = OpTypeInt 64 0
 %s64 = OpTypeInt 64 1
@@ -110,7 +114,32 @@
 )";
   return GenerateShaderCodeImpl(
       body, "OpCapability Int64\n" + capabilities_and_extensions, defintions,
-      memory_model);
+      memory_model, execution);
+}
+
+std::string GenerateShaderComputeCode(
+    const std::string& body,
+    const std::string& capabilities_and_extensions = "",
+    const std::string& memory_model = "GLSL450") {
+  const std::string execution = R"(
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 32 1 1
+)";
+  const std::string defintions = R"(
+%u64 = OpTypeInt 64 0
+%s64 = OpTypeInt 64 1
+
+%u64_1 = OpConstant %u64 1
+%s64_1 = OpConstant %s64 1
+
+%u64_ptr = OpTypePointer Workgroup %u64
+%s64_ptr = OpTypePointer Workgroup %s64
+%u64_var = OpVariable %u64_ptr Workgroup
+%s64_var = OpVariable %s64_ptr Workgroup
+)";
+  return GenerateShaderCodeImpl(
+      body, "OpCapability Int64\n" + capabilities_and_extensions, defintions,
+      memory_model, execution);
 }
 
 std::string GenerateKernelCode(
@@ -217,7 +246,7 @@
 %val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
@@ -341,7 +370,7 @@
 %val2 = OpAtomicLoad %f32 %f32_var %workgroup %acquire
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
@@ -369,8 +398,9 @@
   %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire
   )";
 
-  CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n"),
-                      SPV_ENV_VULKAN_1_0);
+  CompileSuccessfully(
+      GenerateShaderComputeCode(body, "OpCapability Int64Atomics\n"),
+      SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
@@ -380,7 +410,7 @@
   %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire
   )";
 
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("64-bit atomics require the Int64Atomics capability"));
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp
index 579ec69..9a4beba 100644
--- a/test/val/val_barriers_test.cpp
+++ b/test/val/val_barriers_test.cpp
@@ -121,6 +121,15 @@
       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 = "") {
@@ -348,6 +357,8 @@
 
   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-04638"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("ControlBarrier: in Vulkan 1.0 environment Memory Scope is "
@@ -371,10 +382,35 @@
   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 "
                         "cannot be CrossDevice"));
 }
 
+TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeFailure) {
+  const std::string body = R"(
+OpControlBarrier %workgroup %workgroup %acquire
+)";
+
+  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-04639"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Workgroup Memory Scope is limited to MeshNV, TaskNV, "
+                        "and GLCompute execution model"));
+}
+
+TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) {
+  const std::string body = R"(
+OpControlBarrier %workgroup %workgroup %acquire
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
 TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) {
   const std::string body = R"(
 OpControlBarrier %device %device %acquire_and_release_uniform
@@ -647,6 +683,8 @@
 
   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-04638"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("MemoryBarrier: in Vulkan 1.0 environment Memory Scope is "
@@ -1397,6 +1435,8 @@
                       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(