spirv-val: Add Vulkan Execution Scope checks (#4183)

* spirv-val: Add Vulkan Execution Scope checks
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index a92f7fd..29ba583 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -105,21 +105,30 @@
       }
     }
 
-    // If OpControlBarrier is used in fragment, vertex, tessellation evaluation,
-    // or geometry stages, the execution Scope must be Subgroup.
+    // OpControlBarrier must only use Subgroup execution scope for a subset of
+    // execution models.
     if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) {
+      std::string errorVUID = _.VkErrorID(4682);
       _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation([](SpvExecutionModel model,
-                                                std::string* message) {
+          ->RegisterExecutionModelLimitation([errorVUID](
+                                                 SpvExecutionModel model,
+                                                 std::string* message) {
             if (model == SpvExecutionModelFragment ||
                 model == SpvExecutionModelVertex ||
                 model == SpvExecutionModelGeometry ||
-                model == SpvExecutionModelTessellationEvaluation) {
+                model == SpvExecutionModelTessellationEvaluation ||
+                model == SpvExecutionModelRayGenerationKHR ||
+                model == SpvExecutionModelIntersectionKHR ||
+                model == SpvExecutionModelAnyHitKHR ||
+                model == SpvExecutionModelClosestHitKHR ||
+                model == SpvExecutionModelMissKHR) {
               if (message) {
                 *message =
-                    "in Vulkan evironment, OpControlBarrier execution scope "
-                    "must be Subgroup for Fragment, Vertex, Geometry and "
-                    "TessellationEvaluation execution models";
+                    errorVUID +
+                    "in Vulkan environment, OpControlBarrier execution scope "
+                    "must be Subgroup for Fragment, Vertex, Geometry, "
+                    "TessellationEvaluation, RayGeneration, Intersection, "
+                    "AnyHit, ClosestHit, and Miss execution models";
               }
               return false;
             }
@@ -127,11 +136,34 @@
           });
     }
 
+    // Only subset of execution models support Workgroup.
+    if (value == SpvScopeWorkgroup) {
+      std::string errorVUID = _.VkErrorID(4637);
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [errorVUID](SpvExecutionModel model, std::string* message) {
+                if (model != SpvExecutionModelTaskNV &&
+                    model != SpvExecutionModelMeshNV &&
+                    model != SpvExecutionModelTessellationControl &&
+                    model != SpvExecutionModelGLCompute) {
+                  if (message) {
+                    *message =
+                        errorVUID +
+                        "in Vulkan environment, Workgroup execution scope is "
+                        "only for TaskNV, MeshNV, TessellationControl, and "
+                        "GLCompute execution models";
+                  }
+                  return false;
+                }
+                return true;
+              });
+    }
+
     // Vulkan generic rules
     // Scope for execution must be limited to Workgroup or Subgroup
     if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << spvOpcodeString(opcode)
+             << _.VkErrorID(4636) << spvOpcodeString(opcode)
              << ": in Vulkan environment Execution Scope is limited to "
              << "Workgroup and Subgroup";
     }
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 2386a2b..b56d5e0 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1680,6 +1680,10 @@
       return VUID_WRAP(VUID-StandaloneSpirv-None-04634);
     case 4635:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04635);
+    case 4636:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04636);
+    case 4637:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-04637);
     case 4638:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04638);
     case 4639:
@@ -1720,6 +1724,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675);
     case 4677:
       return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
+    case 4682:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
     case 4683:
       return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683);
     case 4685:
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp
index c36bd19..1178ca0 100644
--- a/test/val/val_barriers_test.cpp
+++ b/test/val/val_barriers_test.cpp
@@ -346,6 +346,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-04636"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("ControlBarrier: in Vulkan environment Execution Scope "
                         "is limited to Workgroup and Subgroup"));
 }
@@ -388,9 +390,10 @@
                         "cannot be CrossDevice"));
 }
 
-TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeFailure) {
+TEST_F(ValidateBarriers,
+       OpControlBarrierVulkan1p1WorkgroupNonComputeMemoryFailure) {
   const std::string body = R"(
-OpControlBarrier %workgroup %workgroup %acquire
+OpControlBarrier %subgroup %workgroup %acquire
 )";
 
   CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1);
@@ -402,7 +405,23 @@
                         "and GLCompute execution model"));
 }
 
-TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) {
+TEST_F(ValidateBarriers,
+       OpControlBarrierVulkan1p1WorkgroupNonComputeExecutionFailure) {
+  const std::string body = R"(
+OpControlBarrier %workgroup %subgroup %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-04637"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("in Vulkan environment, Workgroup execution scope is "
+                        "only for TaskNV, MeshNV, TessellationControl, and "
+                        "GLCompute execution models"));
+}
+
+TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupComputeSuccess) {
   const std::string body = R"(
 OpControlBarrier %workgroup %workgroup %acquire
 )";
@@ -411,6 +430,15 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
 }
 
+TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) {
+  const std::string body = R"(
+OpControlBarrier %subgroup %subgroup %acquire
+)";
+
+  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
@@ -483,9 +511,13 @@
                       SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpControlBarrier execution scope must be Subgroup for "
-                        "Fragment, Vertex, Geometry and TessellationEvaluation "
-                        "execution models"));
+              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) {
@@ -521,9 +553,13 @@
                       SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpControlBarrier execution scope must be Subgroup for "
-                        "Fragment, Vertex, Geometry and TessellationEvaluation "
-                        "execution models"));
+              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) {
@@ -561,9 +597,13 @@
       SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpControlBarrier execution scope must be Subgroup for "
-                        "Fragment, Vertex, Geometry and TessellationEvaluation "
-                        "execution models"));
+              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) {
@@ -604,9 +644,13 @@
                       SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("OpControlBarrier execution scope must be Subgroup for "
-                        "Fragment, Vertex, Geometry and TessellationEvaluation "
-                        "execution models"));
+              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,
@@ -1488,6 +1532,8 @@
 
   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"));
 }
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index cbf6d7c..096fd17 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -4992,6 +4992,8 @@
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-04636"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr(": in Vulkan environment Execution Scope is limited to "
                         "Workgroup and Subgroup"));
 }