Restrict capabilities to WebGPU spec (#2113)

Restrict capabilities to WebGPU spec

This covers whitelisting Matrix, Shader, Sampled1D, Image1D,
DerivativeControl, and ImageQuery. These are the allowed capabilities
that don't require an extension. Whitelisting VulkanMemoryModelKHR
will be handled by whitelisting its extension in a seperate patch.

Fixes #2101
diff --git a/source/val/validate_capability.cpp b/source/val/validate_capability.cpp
index 3c5f98b..b464d5d 100644
--- a/source/val/validate_capability.cpp
+++ b/source/val/validate_capability.cpp
@@ -220,6 +220,19 @@
   return false;
 }
 
+bool IsSupportGuaranteedWebGPU(uint32_t capability) {
+  switch (capability) {
+    case SpvCapabilityMatrix:
+    case SpvCapabilityShader:
+    case SpvCapabilitySampled1D:
+    case SpvCapabilityImage1D:
+    case SpvCapabilityDerivativeControl:
+    case SpvCapabilityImageQuery:
+      return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 // Validates that capability declarations use operands allowed in the current
@@ -303,6 +316,14 @@
              << " Profile specification"
              << " (or requires extension or capability)";
     }
+  } else if (env == SPV_ENV_WEBGPU_0) {
+    if (!IsSupportGuaranteedWebGPU(capability) &&
+        !IsEnabledByExtension(_, capability)) {
+      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+             << "Capability " << capability_str()
+             << " is not allowed by WebGPU specification"
+             << " (or requires extension)";
+    }
   }
 
   return SPV_SUCCESS;
diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp
index 923d417..46a8030 100644
--- a/test/val/val_capability_test.cpp
+++ b/test/val/val_capability_test.cpp
@@ -112,6 +112,8 @@
 using ValidateCapabilityOpenGL40 = spvtest::ValidateBase<CapTestParameter>;
 // Always assembles using Vulkan 1.1.
 using ValidateCapabilityVulkan11 = spvtest::ValidateBase<CapTestParameter>;
+// Always assembles using WebGPU.
+using ValidateCapabilityWebGPU = spvtest::ValidateBase<CapTestParameter>;
 
 TEST_F(ValidateCapability, Default) {
   const char str[] = R"(
@@ -382,6 +384,18 @@
   return *r;
 }
 
+const std::vector<std::string>& AllWebGPUCapabilities() {
+  static const auto r = new std::vector<std::string>{
+    "",
+    "Shader",
+    "Matrix",
+    "Sampled1D",
+    "Image1D",
+    "ImageQuery",
+    "DerivativeControl"};
+    return *r;
+}
+
 const std::vector<std::string>& MatrixDependencies() {
   static const auto r = new std::vector<std::string>{
   "Matrix",
@@ -572,6 +586,12 @@
   " OpCapability Shader"
   " OpMemoryModel Logical GLSL450 ";
 
+const char kVulkanMemoryModel[] = \
+  " OpCapability Shader"
+  " OpCapability VulkanMemoryModelKHR"
+  " OpExtension \"SPV_KHR_vulkan_memory_model\""
+  " OpMemoryModel Logical VulkanKHR ";
+
 const char kVoidFVoid[] = \
   " %void   = OpTypeVoid"
   " %void_f = OpTypeFunction %void"
@@ -1529,6 +1549,16 @@
           AllSpirV10Capabilities())
 )),);
 
+INSTANTIATE_TEST_CASE_P(Capabilities, ValidateCapabilityWebGPU,
+                        Combine(
+                            // All capabilities to try.
+                            ValuesIn(AllCapabilities()),
+                            Values(
+std::make_pair(std::string(kVulkanMemoryModel) +
+          "OpEntryPoint Vertex %func \"shader\" \n" + std::string(kVoidFVoid),
+          AllWebGPUCapabilities())
+)),);
+
 INSTANTIATE_TEST_CASE_P(Capabilities, ValidateCapabilityVulkan11,
                         Combine(
                             // All capabilities to try.
@@ -1720,6 +1750,17 @@
   }
 }
 
+TEST_P(ValidateCapabilityWebGPU, Capability) {
+  const std::string capability = Capability(GetParam());
+  if (Exists(capability, SPV_ENV_WEBGPU_0)) {
+    const std::string test_code = MakeAssembly(GetParam());
+    CompileSuccessfully(test_code, SPV_ENV_WEBGPU_0);
+    ASSERT_EQ(ExpectedResult(GetParam()),
+              ValidateInstructions(SPV_ENV_WEBGPU_0))
+        << test_code;
+  }
+}
+
 TEST_F(ValidateCapability, SemanticsIdIsAnIdNotALiteral) {
   // From https://github.com/KhronosGroup/SPIRV-Tools/issues/248
   // The validator was interpreting the memory semantics ID number
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 554c4b3..bbf7e94 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -151,7 +151,6 @@
 TEST_F(ValidateDecorations, WebGPUOpDecorationGroupBad) {
   std::string spirv = R"(
                OpCapability Shader
-               OpCapability Linkage
                OpCapability VulkanMemoryModelKHR
                OpExtension "SPV_KHR_vulkan_memory_model"
                OpMemoryModel Logical VulkanKHR
diff --git a/test/val/val_webgpu_test.cpp b/test/val/val_webgpu_test.cpp
index 31da795..cc3f905 100644
--- a/test/val/val_webgpu_test.cpp
+++ b/test/val/val_webgpu_test.cpp
@@ -29,13 +29,19 @@
 
 TEST_F(ValidateWebGPU, OpUndefIsDisallowed) {
   std::string spirv = R"(
-    OpCapability Shader
-    OpCapability Linkage
-    OpCapability VulkanMemoryModelKHR
-    OpExtension "SPV_KHR_vulkan_memory_model"
-    OpMemoryModel Logical VulkanKHR
-    %float = OpTypeFloat 32
-    %1 = OpUndef %float
+          OpCapability Shader
+          OpCapability VulkanMemoryModelKHR
+          OpExtension "SPV_KHR_vulkan_memory_model"
+          OpMemoryModel Logical VulkanKHR
+          OpEntryPoint Vertex %func "shader"
+%float  = OpTypeFloat 32
+%1      = OpUndef %float
+%void   = OpTypeVoid
+%void_f = OpTypeFunction %void
+%func   = OpFunction %void None %void_f
+%label  = OpLabel
+          OpReturn
+          OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
@@ -51,7 +57,6 @@
 TEST_F(ValidateWebGPU, OpNameIsDisallowed) {
   std::string spirv = R"(
      OpCapability Shader
-     OpCapability Linkage
      OpCapability VulkanMemoryModelKHR
      OpExtension "SPV_KHR_vulkan_memory_model"
      OpMemoryModel Logical VulkanKHR
@@ -70,7 +75,6 @@
 TEST_F(ValidateWebGPU, OpMemberNameIsDisallowed) {
   std::string spirv = R"(
      OpCapability Shader
-     OpCapability Linkage
      OpCapability VulkanMemoryModelKHR
      OpExtension "SPV_KHR_vulkan_memory_model"
      OpMemoryModel Logical VulkanKHR
@@ -91,7 +95,6 @@
 TEST_F(ValidateWebGPU, OpSourceIsDisallowed) {
   std::string spirv = R"(
      OpCapability Shader
-     OpCapability Linkage
      OpCapability VulkanMemoryModelKHR
      OpExtension "SPV_KHR_vulkan_memory_model"
      OpMemoryModel Logical VulkanKHR
@@ -112,7 +115,6 @@
 TEST_F(ValidateWebGPU, OpSourceExtensionIsDisallowed) {
   std::string spirv = R"(
      OpCapability Shader
-     OpCapability Linkage
      OpCapability VulkanMemoryModelKHR
      OpExtension "SPV_KHR_vulkan_memory_model"
      OpMemoryModel Logical VulkanKHR
@@ -131,7 +133,6 @@
 TEST_F(ValidateWebGPU, OpStringIsDisallowed) {
   std::string spirv = R"(
      OpCapability Shader
-     OpCapability Linkage
      OpCapability VulkanMemoryModelKHR
      OpExtension "SPV_KHR_vulkan_memory_model"
      OpMemoryModel Logical VulkanKHR
@@ -152,7 +153,6 @@
 TEST_F(ValidateWebGPU, OpNoLineDisallowed) {
   std::string spirv = R"(
      OpCapability Shader
-     OpCapability Linkage
      OpCapability VulkanMemoryModelKHR
      OpExtension "SPV_KHR_vulkan_memory_model"
      OpMemoryModel Logical VulkanKHR
@@ -169,11 +169,17 @@
 
 TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
   std::string spirv = R"(
-     OpCapability Shader
-     OpCapability Linkage
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Logical VulkanKHR
+          OpCapability Shader
+          OpCapability VulkanMemoryModelKHR
+          OpExtension "SPV_KHR_vulkan_memory_model"
+          OpMemoryModel Logical VulkanKHR
+          OpEntryPoint Vertex %func "shader"
+%void   = OpTypeVoid
+%void_f = OpTypeFunction %void
+%func   = OpFunction %void None %void_f
+%label  = OpLabel
+          OpReturn
+          OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
@@ -184,7 +190,6 @@
 TEST_F(ValidateWebGPU, NonLogicalAddressingModelBad) {
   std::string spirv = R"(
      OpCapability Shader
-     OpCapability Linkage
      OpCapability VulkanMemoryModelKHR
      OpExtension "SPV_KHR_vulkan_memory_model"
      OpMemoryModel Physical32 VulkanKHR
@@ -202,7 +207,6 @@
 TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelBad) {
   std::string spirv = R"(
      OpCapability Shader
-     OpCapability Linkage
      OpMemoryModel Logical GLSL450
      OpNoLine
 )";