Revert "Remove SubgroupsF16 feature and subgroups_f16 WGSL extension"

This reverts commit 7bb9a04ba94d8f4b40e8dbb2d000199d75cc9453.

Reason for revert: Broke rolls into g3 and Chromium. ML code depends on GPUFeatureName::SubroupsF16

Bug: 380244620, 390632529, 383606929
Original change's description:
> Remove SubgroupsF16 feature and subgroups_f16 WGSL extension
>
> Bug: 380244620, 390632529, 383606929
> Change-Id: Ie93be38eda90fb445c443f37466a01e67b071a0e
> Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/235454
> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
> Reviewed-by: David Neto <dneto@google.com>
> Commit-Queue: Fr <beaufort.francois@gmail.com>

Bug: 380244620, 390632529, 383606929
Change-Id: I4e4e98ce2f756c9593184d0ae041c1aae83b53d9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/236834
Commit-Queue: David Neto <dneto@google.com>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index fed5999..7d59db0 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -2270,58 +2270,59 @@
             {"value": 5, "name": "chromium experimental immediate data", "tags": ["dawn"]},
             {"value": 6, "name": "transient attachments", "tags": ["dawn"]},
             {"value": 7, "name": "MSAA render to single sampled", "tags": ["dawn"]},
-            {"value": 8, "name": "D3D11 multithread protected", "tags": ["dawn", "native"]},
-            {"value": 9, "name": "ANGLE texture sharing", "tags": ["dawn", "native"]},
-            {"value": 10, "name": "pixel local storage coherent", "tags": ["dawn"]},
-            {"value": 11, "name": "pixel local storage non coherent", "tags": ["dawn"]},
-            {"value": 12, "name": "unorm16 texture formats", "tags": ["dawn", "emscripten"], "jsrepr": "'chromium-experimental-unorm16-texture-formats'"},
-            {"value": 13, "name": "snorm16 texture formats", "tags": ["dawn", "emscripten"], "jsrepr": "'chromium-experimental-snorm16-texture-formats'"},
-            {"value": 14, "name": "multi planar format extended usages", "tags": ["dawn"]},
-            {"value": 15, "name": "multi planar format p010", "tags": ["dawn"]},
-            {"value": 16, "name": "host mapped pointer", "tags": ["dawn"]},
-            {"value": 17, "name": "multi planar render targets", "tags": ["dawn"]},
-            {"value": 18, "name": "multi planar format nv12a", "tags": ["dawn"]},
-            {"value": 19, "name": "framebuffer fetch", "tags": ["dawn"]},
-            {"value": 20, "name": "buffer map extended usages", "tags": ["dawn"]},
-            {"value": 21, "name": "adapter properties memory heaps", "tags": ["dawn"]},
-            {"value": 22, "name": "adapter properties D3D", "tags": ["dawn"]},
-            {"value": 23, "name": "adapter properties vk", "tags": ["dawn"]},
-            {"value": 24, "name": "r8 unorm storage", "tags": ["dawn"]},
-            {"value": 25, "name": "dawn format capabilities", "tags": ["dawn"]},
-            {"value": 26, "name": "dawn drm format capabilities", "tags": ["dawn"]},
-            {"value": 27, "name": "norm16 texture formats", "tags": ["dawn"]},
-            {"value": 28, "name": "multi planar format nv16", "tags": ["dawn"]},
-            {"value": 29, "name": "multi planar format nv24", "tags": ["dawn"]},
-            {"value": 30, "name": "multi planar format p210", "tags": ["dawn"]},
-            {"value": 31, "name": "multi planar format p410", "tags": ["dawn"]},
+            {"value": 8, "name": "subgroups f16", "tags": ["dawn", "emscripten", "deprecated"]},
+            {"value": 9, "name": "D3D11 multithread protected", "tags": ["dawn", "native"]},
+            {"value": 10, "name": "ANGLE texture sharing", "tags": ["dawn", "native"]},
+            {"value": 11, "name": "pixel local storage coherent", "tags": ["dawn"]},
+            {"value": 12, "name": "pixel local storage non coherent", "tags": ["dawn"]},
+            {"value": 13, "name": "unorm16 texture formats", "tags": ["dawn", "emscripten"], "jsrepr": "'chromium-experimental-unorm16-texture-formats'"},
+            {"value": 14, "name": "snorm16 texture formats", "tags": ["dawn", "emscripten"], "jsrepr": "'chromium-experimental-snorm16-texture-formats'"},
+            {"value": 15, "name": "multi planar format extended usages", "tags": ["dawn"]},
+            {"value": 16, "name": "multi planar format p010", "tags": ["dawn"]},
+            {"value": 17, "name": "host mapped pointer", "tags": ["dawn"]},
+            {"value": 18, "name": "multi planar render targets", "tags": ["dawn"]},
+            {"value": 19, "name": "multi planar format nv12a", "tags": ["dawn"]},
+            {"value": 20, "name": "framebuffer fetch", "tags": ["dawn"]},
+            {"value": 21, "name": "buffer map extended usages", "tags": ["dawn"]},
+            {"value": 22, "name": "adapter properties memory heaps", "tags": ["dawn"]},
+            {"value": 23, "name": "adapter properties D3D", "tags": ["dawn"]},
+            {"value": 24, "name": "adapter properties vk", "tags": ["dawn"]},
+            {"value": 25, "name": "r8 unorm storage", "tags": ["dawn"]},
+            {"value": 26, "name": "dawn format capabilities", "tags": ["dawn"]},
+            {"value": 27, "name": "dawn drm format capabilities", "tags": ["dawn"]},
+            {"value": 28, "name": "norm16 texture formats", "tags": ["dawn"]},
+            {"value": 29, "name": "multi planar format nv16", "tags": ["dawn"]},
+            {"value": 30, "name": "multi planar format nv24", "tags": ["dawn"]},
+            {"value": 31, "name": "multi planar format p210", "tags": ["dawn"]},
+            {"value": 32, "name": "multi planar format p410", "tags": ["dawn"]},
 
-            {"value": 32, "name": "shared texture memory vk dedicated allocation", "tags": ["dawn", "native"]},
-            {"value": 33, "name": "shared texture memory a hardware buffer", "tags": ["dawn", "native"]},
-            {"value": 34, "name": "shared texture memory dma buf", "tags": ["dawn", "native"]},
-            {"value": 35, "name": "shared texture memory opaque FD", "tags": ["dawn", "native"]},
-            {"value": 36, "name": "shared texture memory zircon handle", "tags": ["dawn", "native"]},
-            {"value": 37, "name": "shared texture memory DXGI shared handle", "tags": ["dawn", "native"]},
-            {"value": 38, "name": "shared texture memory D3D11 texture 2D", "tags": ["dawn", "native"]},
-            {"value": 39, "name": "shared texture memory IO surface", "tags": ["dawn", "native"]},
-            {"value": 40, "name": "shared texture memory EGL image", "tags": ["dawn", "native"]},
-            {"value": 41, "name": "shared fence vk semaphore opaque FD", "tags": ["dawn", "native"]},
-            {"value": 42, "name": "shared fence sync FD", "tags": ["dawn", "native"]},
-            {"value": 43, "name": "shared fence vk semaphore zircon handle", "tags": ["dawn", "native"]},
-            {"value": 44, "name": "shared fence DXGI shared handle", "tags": ["dawn", "native"]},
-            {"value": 45, "name": "shared fence MTL shared event", "tags": ["dawn", "native"]},
-            {"value": 46, "name": "shared buffer memory D3D12 resource", "tags": ["dawn", "native"]},
-            {"value": 47, "name": "static samplers", "tags": ["dawn"]},
-            {"value": 48, "name": "y cb cr vulkan samplers", "tags": ["dawn"]},
-            {"value": 49, "name": "shader module compilation options", "tags": ["dawn"]},
+            {"value": 33, "name": "shared texture memory vk dedicated allocation", "tags": ["dawn", "native"]},
+            {"value": 34, "name": "shared texture memory a hardware buffer", "tags": ["dawn", "native"]},
+            {"value": 35, "name": "shared texture memory dma buf", "tags": ["dawn", "native"]},
+            {"value": 36, "name": "shared texture memory opaque FD", "tags": ["dawn", "native"]},
+            {"value": 37, "name": "shared texture memory zircon handle", "tags": ["dawn", "native"]},
+            {"value": 38, "name": "shared texture memory DXGI shared handle", "tags": ["dawn", "native"]},
+            {"value": 39, "name": "shared texture memory D3D11 texture 2D", "tags": ["dawn", "native"]},
+            {"value": 40, "name": "shared texture memory IO surface", "tags": ["dawn", "native"]},
+            {"value": 41, "name": "shared texture memory EGL image", "tags": ["dawn", "native"]},
+            {"value": 42, "name": "shared fence vk semaphore opaque FD", "tags": ["dawn", "native"]},
+            {"value": 43, "name": "shared fence sync FD", "tags": ["dawn", "native"]},
+            {"value": 44, "name": "shared fence vk semaphore zircon handle", "tags": ["dawn", "native"]},
+            {"value": 45, "name": "shared fence DXGI shared handle", "tags": ["dawn", "native"]},
+            {"value": 46, "name": "shared fence MTL shared event", "tags": ["dawn", "native"]},
+            {"value": 47, "name": "shared buffer memory D3D12 resource", "tags": ["dawn", "native"]},
+            {"value": 48, "name": "static samplers", "tags": ["dawn"]},
+            {"value": 49, "name": "y cb cr vulkan samplers", "tags": ["dawn"]},
+            {"value": 50, "name": "shader module compilation options", "tags": ["dawn"]},
 
-            {"value": 50, "name": "dawn load resolve texture", "tags": ["dawn"]},
-            {"value": 51, "name": "dawn partial load resolve texture", "tags": ["dawn"]},
-            {"value": 52, "name": "multi draw indirect", "tags": ["dawn", "emscripten"], "jsrepr": "'chromium-experimental-multi-draw-indirect'"},
-            {"value": 53, "name": "dawn texel copy buffer row alignment", "tags": ["dawn"]},
-            {"value": 54, "name": "flexible texture views", "tags": ["dawn"]},
-            {"value": 55, "name": "chromium experimental subgroup matrix", "tags": ["dawn"]},
-            {"value": 56, "name": "shared fence EGL sync", "tags": ["dawn", "native"]},
-            {"value": 57, "name": "dawn device allocator control", "tags": ["dawn"]}
+            {"value": 51, "name": "dawn load resolve texture", "tags": ["dawn"]},
+            {"value": 52, "name": "dawn partial load resolve texture", "tags": ["dawn"]},
+            {"value": 53, "name": "multi draw indirect", "tags": ["dawn", "emscripten"], "jsrepr": "'chromium-experimental-multi-draw-indirect'"},
+            {"value": 54, "name": "dawn texel copy buffer row alignment", "tags": ["dawn"]},
+            {"value": 55, "name": "flexible texture views", "tags": ["dawn"]},
+            {"value": 56, "name": "chromium experimental subgroup matrix", "tags": ["dawn"]},
+            {"value": 57, "name": "shared fence EGL sync", "tags": ["dawn", "native"]},
+            {"value": 58, "name": "dawn device allocator control", "tags": ["dawn"]}
         ]
     },
     "filter mode": {
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 6be0557..58b245b 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -337,6 +337,17 @@
         DAWN_INVALID_IF(!result.success, "Invalid feature required: %s",
                         result.errorMessage.c_str());
     }
+    // Validate features dependency.
+    // TODO(349125474): Decide if this validation is needed, see
+    // https://github.com/gpuweb/gpuweb/issues/4734 for detail.
+    if (requiredFeatureSet.count(wgpu::FeatureName::SubgroupsF16) > 0) {
+        DAWN_INVALID_IF((requiredFeatureSet.count(wgpu::FeatureName::Subgroups) == 0),
+                        "Feature %s must be required together with feature %s.",
+                        wgpu::FeatureName::SubgroupsF16, wgpu::FeatureName::Subgroups);
+        DAWN_INVALID_IF(requiredFeatureSet.count(wgpu::FeatureName::ShaderF16) == 0,
+                        "Feature %s must be required together with feature %s.",
+                        wgpu::FeatureName::SubgroupsF16, wgpu::FeatureName::ShaderF16);
+    }
 
     if (descriptor->requiredLimits != nullptr) {
         CombinedLimits requiredLimits;
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 6c264ac..e6f0c63 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -1649,6 +1649,9 @@
     if (mEnabledFeatures.IsEnabled(Feature::Subgroups)) {
         mWGSLAllowedFeatures.extensions.insert(tint::wgsl::Extension::kSubgroups);
     }
+    if (mEnabledFeatures.IsEnabled(Feature::SubgroupsF16)) {
+        mWGSLAllowedFeatures.extensions.insert(tint::wgsl::Extension::kSubgroupsF16);
+    }
     if (IsToggleEnabled(Toggle::AllowUnsafeAPIs)) {
         mWGSLAllowedFeatures.extensions.insert(
             tint::wgsl::Extension::kChromiumDisableUniformityAnalysis);
diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp
index 469f201..0698eb5 100644
--- a/src/dawn/native/Features.cpp
+++ b/src/dawn/native/Features.cpp
@@ -372,6 +372,10 @@
      {"Supports the \"enable subgroups;\" directive in WGSL.",
       "https://github.com/gpuweb/gpuweb/blob/main/proposals/subgroups.md",
       FeatureInfo::FeatureState::Stable}},
+    {Feature::SubgroupsF16,
+     {"Supports the \"enable subgroups_f16;\" directive in WGSL (deprecated).",
+      "https://github.com/gpuweb/gpuweb/blob/main/proposals/subgroups.md",
+      FeatureInfo::FeatureState::Stable}},
     {Feature::CoreFeaturesAndLimits,
      {"Lifts all compatibility mode restrictions (features and limits) to core when enabled on a "
       "device.",
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index 700c125..5058d86 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -175,9 +175,11 @@
 
     // ShaderF16 features require DXC version being 1.4 or higher, shader model supporting 6.2 or
     // higher, and native supporting F16 shader ops.
+    bool shaderF16Enabled = false;
     if (GetBackend()->IsDXCAvailableAndVersionAtLeast(1, 4, 1, 4) &&
         mDeviceInfo.highestSupportedShaderModel >= 62 && mDeviceInfo.supportsNative16BitShaderOps) {
         EnableFeature(Feature::ShaderF16);
+        shaderF16Enabled = true;
     }
 
     // The function subgroupBroadcast(f16) fails for some edge cases on intel gen-9 devices.
@@ -186,6 +188,11 @@
     // Subgroups feature requires SM >= 6.0 and capabilities flags.
     if (!kForceDisableSubgroups && GetBackend()->IsDXCAvailable() && mDeviceInfo.supportsWaveOps) {
         EnableFeature(Feature::Subgroups);
+        // D3D12 devices that support both native f16 and wave ops can support subgroups-f16.
+        // TODO(crbug.com/380244620): Remove when 'subgroups_f16' has been fully deprecated.
+        if (shaderF16Enabled) {
+            EnableFeature(Feature::SubgroupsF16);
+        }
     }
 
     D3D12_FEATURE_DATA_FORMAT_SUPPORT bgra8unormFormatInfo = {};
@@ -411,6 +418,7 @@
         switch (feature) {
             case wgpu::FeatureName::ShaderF16:
             case wgpu::FeatureName::Subgroups:
+            case wgpu::FeatureName::SubgroupsF16:
                 return FeatureValidationResult(
                     absl::StrFormat("Feature %s requires DXC for D3D12.", feature));
             default:
@@ -419,8 +427,9 @@
     }
     // Validate applied shader version.
     switch (feature) {
-        // The feature `shader-f16` requires using shader model 6.2 or higher.
-        case wgpu::FeatureName::ShaderF16: {
+        // The feature `shader-f16` and `subgroups-f16` requires using shader model 6.2 or higher.
+        case wgpu::FeatureName::ShaderF16:
+        case wgpu::FeatureName::SubgroupsF16: {
             if (!(GetAppliedShaderModelUnderToggles(toggles) >= 62)) {
                 return FeatureValidationResult(absl::StrFormat(
                     "Feature %s requires shader model 6.2 or higher for D3D12.", feature));
diff --git a/src/dawn/native/metal/PhysicalDeviceMTL.mm b/src/dawn/native/metal/PhysicalDeviceMTL.mm
index 8281b36..98f4525 100644
--- a/src/dawn/native/metal/PhysicalDeviceMTL.mm
+++ b/src/dawn/native/metal/PhysicalDeviceMTL.mm
@@ -676,6 +676,8 @@
     if (!kForceDisableSubgroups && ([*mDevice supportsFamily:MTLGPUFamilyApple6] ||
                                     [*mDevice supportsFamily:MTLGPUFamilyMac2])) {
         EnableFeature(Feature::Subgroups);
+        // TODO(crbug.com/380244620) remove SubgroupsF16
+        EnableFeature(Feature::SubgroupsF16);
     }
 
     if ([*mDevice supportsFamily:MTLGPUFamilyApple7]) {
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index 9719550..34d2db2 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -532,9 +532,12 @@
     }
 
     // Set device feature for subgroups with f16 types.
-    if (HasFeature(Feature::ShaderF16) && HasFeature(Feature::Subgroups)) {
+    if (HasFeature(Feature::SubgroupsF16) ||
+        (HasFeature(Feature::ShaderF16) && HasFeature(Feature::Subgroups))) {
         DAWN_ASSERT(usedKnobs.HasExt(DeviceExt::ShaderSubgroupExtendedTypes) &&
-                    mDeviceInfo.shaderSubgroupExtendedTypes.shaderSubgroupExtendedTypes == VK_TRUE);
+                    mDeviceInfo.shaderSubgroupExtendedTypes.shaderSubgroupExtendedTypes ==
+                        VK_TRUE &&
+                    HasFeature(Feature::ShaderF16) && HasFeature(Feature::Subgroups));
 
         usedKnobs.shaderSubgroupExtendedTypes = mDeviceInfo.shaderSubgroupExtendedTypes;
         featuresChain.Add(&usedKnobs.shaderSubgroupExtendedTypes);
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index 62e8bbc..a10620b 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -409,8 +409,7 @@
     //    and quad bits, and
     // 4. VK_EXT_subgroup_size_control extension is valid, and both subgroupSizeControl
     //    and computeFullSubgroups is TRUE in VkPhysicalDeviceSubgroupSizeControlFeaturesEXT.
-    const bool hasBaseSubgroupSupport =
-        (mDeviceInfo.properties.apiVersion >= VK_API_VERSION_1_1) &&
+    if (!kForceDisableSubgroups && (mDeviceInfo.properties.apiVersion >= VK_API_VERSION_1_1) &&
         (mDeviceInfo.subgroupProperties.supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) &&
         (mDeviceInfo.subgroupProperties.supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) &&
         (mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) &&
@@ -422,18 +421,26 @@
         (mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) &&
         (mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) &&
         (mDeviceInfo.subgroupSizeControlFeatures.subgroupSizeControl == VK_TRUE) &&
-        (mDeviceInfo.subgroupSizeControlFeatures.computeFullSubgroups == VK_TRUE);
-
-    // If shader f16 is enabled we only enable subgroups if we extended subgroup support.
-    // This means there is a vary narrow number of devices (~4%) will not get subgroup
-    // support due to the fact that they support shader f16 but not actually f16
-    // operations in subgroups.
-    const bool hasRequiredF16Support =
-        !shaderF16Enabled ||
-        (mDeviceInfo.shaderSubgroupExtendedTypes.shaderSubgroupExtendedTypes == VK_TRUE);
-
-    if (!kForceDisableSubgroups && hasBaseSubgroupSupport && hasRequiredF16Support) {
-        EnableFeature(Feature::Subgroups);
+        (mDeviceInfo.subgroupSizeControlFeatures.computeFullSubgroups == VK_TRUE)) {
+        if (shaderF16Enabled) {
+            if (mDeviceInfo.shaderSubgroupExtendedTypes.shaderSubgroupExtendedTypes == VK_TRUE) {
+                // Enable SubgroupsF16 feature if:
+                // 1. Subgroups feature is enabled, and
+                // 2. ShaderF16 feature is enabled, and
+                // 3. shaderSubgroupExtendedTypes is TRUE in
+                //    VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR.
+                // TODO(crbug.com/380244620): Remove when 'subgroups_f16' has been fully deprecated.
+                EnableFeature(Feature::SubgroupsF16);
+                // If shader f16 is enable we only enable subgroups if we extended subgroup support.
+                // This means there is a vary narrow number of devices (~4%) will not get subgroup
+                // support due to the fact that they support shader f16 but not actually f16
+                // operations in subgroups.
+                EnableFeature(Feature::Subgroups);
+            }
+        } else {
+            // Subgroups without extended type support (f16).
+            EnableFeature(Feature::Subgroups);
+        }
     }
 
     // Enable subgroup matrix if all of the following are true:
diff --git a/src/dawn/node/binding/Converter.cpp b/src/dawn/node/binding/Converter.cpp
index 3b5e852..23e5463 100644
--- a/src/dawn/node/binding/Converter.cpp
+++ b/src/dawn/node/binding/Converter.cpp
@@ -1621,6 +1621,7 @@
         case wgpu::FeatureName::SharedTextureMemoryVkDedicatedAllocation:
         case wgpu::FeatureName::SharedTextureMemoryZirconHandle:
         case wgpu::FeatureName::StaticSamplers:
+        case wgpu::FeatureName::SubgroupsF16:
         case wgpu::FeatureName::TextureCompressionBCSliced3D:
         case wgpu::FeatureName::TextureCompressionASTCSliced3D:
         case wgpu::FeatureName::TransientAttachments:
diff --git a/src/dawn/tests/end2end/SubgroupsTests.cpp b/src/dawn/tests/end2end/SubgroupsTests.cpp
index e8b0423..620f320 100644
--- a/src/dawn/tests/end2end/SubgroupsTests.cpp
+++ b/src/dawn/tests/end2end/SubgroupsTests.cpp
@@ -196,6 +196,16 @@
             mRequiredSubgroupsFeature = true;
             requiredFeatures.push_back(wgpu::FeatureName::Subgroups);
         }
+        if (SupportsFeatures({wgpu::FeatureName::SubgroupsF16})) {
+            // SubgroupsF16 feature could be supported only if ShaderF16 and Subgroups features
+            // are also supported.
+            DAWN_ASSERT(mRequiredShaderF16Feature && mRequiredSubgroupsFeature);
+            mRequiredSubgroupsF16Feature = true;
+            requiredFeatures.push_back(wgpu::FeatureName::SubgroupsF16);
+        }
+
+        mSubgroupsF16SupportedByBackend = SupportsFeatures({wgpu::FeatureName::SubgroupsF16});
+
         return requiredFeatures;
     }
 
@@ -207,15 +217,23 @@
         if (mRequiredSubgroupsFeature) {
             code << "enable subgroups;";
         }
+        if (mRequiredSubgroupsF16Feature) {
+            code << "enable subgroups_f16;";
+        }
         return code;
     }
 
     bool IsShaderF16EnabledInWGSL() const { return mRequiredShaderF16Feature; }
     bool IsSubgroupsEnabledInWGSL() const { return mRequiredSubgroupsFeature; }
+    bool IsSubgroupsF16EnabledInWGSL() const { return mRequiredSubgroupsF16Feature; }
+    bool IsSubgroupsF16SupportedByBackend() const { return mSubgroupsF16SupportedByBackend; }
 
   private:
     bool mRequiredShaderF16Feature = false;
     bool mRequiredSubgroupsFeature = false;
+    bool mRequiredSubgroupsF16Feature = false;
+    // Indicates that backend actually supports using subgroups functions with f16 types.
+    bool mSubgroupsF16SupportedByBackend = false;
 };
 
 class SubgroupsShaderTests : public SubgroupsTestsBase<AdapterTestParam> {
@@ -696,9 +714,12 @@
 // and 256. Note that although we assume invocation 0 of the workgroup has a subgroup_id of 0 in its
 // subgroup, we don't assume any other particular subgroups layout property.
 TEST_P(SubgroupsBroadcastTests, SubgroupBroadcast) {
-    DAWN_TEST_UNSUPPORTED_IF(!IsSubgroupsEnabledInWGSL());
     if (GetParam().mBroadcastType == BroadcastType::F16) {
-        DAWN_TEST_UNSUPPORTED_IF(!IsShaderF16EnabledInWGSL());
+        DAWN_TEST_UNSUPPORTED_IF(!IsSubgroupsF16SupportedByBackend());
+        DAWN_ASSERT(IsShaderF16EnabledInWGSL() && IsSubgroupsEnabledInWGSL() &&
+                    IsSubgroupsF16EnabledInWGSL());
+    } else {
+        DAWN_TEST_UNSUPPORTED_IF(!IsSubgroupsEnabledInWGSL());
     }
 
     for (uint32_t workgroupSize : {1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128, 255, 256}) {
@@ -886,9 +907,8 @@
 };
 
 TEST_P(SubgroupsShaderInclusiveTest, InclusiveExecution) {
-    DAWN_TEST_UNSUPPORTED_IF(!IsSubgroupsEnabledInWGSL());
     if (GetParam().mSubgroupOpDataType == SubgroupOpDataType::F16) {
-        DAWN_TEST_UNSUPPORTED_IF(!IsShaderF16EnabledInWGSL());
+        DAWN_TEST_UNSUPPORTED_IF(!IsSubgroupsF16SupportedByBackend());
 
         // TODO(361330160): The f16 implementation does not seem produce correct values in
         // execution. It is not clear if this is due to something wrong with the polyfill or the
@@ -899,6 +919,11 @@
         // TODO(361330160): Also fails on MacBookPro16,1 with AMD Radeon Pro 5300M
         DAWN_SUPPRESS_TEST_IF(gpu_info::IsAMDRDNA1(GetParam().adapterProperties.vendorID,
                                                    GetParam().adapterProperties.deviceID));
+
+        DAWN_ASSERT(IsShaderF16EnabledInWGSL() && IsSubgroupsEnabledInWGSL() &&
+                    IsSubgroupsF16EnabledInWGSL());
+    } else {
+        DAWN_TEST_UNSUPPORTED_IF(!IsSubgroupsEnabledInWGSL());
     }
 
     for (uint32_t workgroupSize : {1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128, 255, 256}) {
diff --git a/src/dawn/tests/perf_tests/MatrixVectorMultiplyPerf.cpp b/src/dawn/tests/perf_tests/MatrixVectorMultiplyPerf.cpp
index 3d18958..dfba797 100644
--- a/src/dawn/tests/perf_tests/MatrixVectorMultiplyPerf.cpp
+++ b/src/dawn/tests/perf_tests/MatrixVectorMultiplyPerf.cpp
@@ -98,6 +98,7 @@
     std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
         mUsingF16 = false;
         mUsingSubgroups = false;
+        mUsingSubgroupsF16 = false;
         mAllFeaturesSupported = true;
 
         auto requirements =
@@ -116,6 +117,10 @@
         if (GetParam().mImpl == KernelImplementation::Subgroup) {
             mUsingSubgroups = true;
             requireFeature(wgpu::FeatureName::Subgroups);
+            if (mUsingF16) {
+                mUsingSubgroupsF16 = true;
+                requireFeature(wgpu::FeatureName::SubgroupsF16);
+            }
         }
         return requirements;
     }
@@ -148,6 +153,7 @@
 
     bool mUsingF16;
     bool mUsingSubgroups;
+    bool mUsingSubgroupsF16;
     bool mAllFeaturesSupported;
 };
 
@@ -171,7 +177,7 @@
     DAWN_TEST_UNSUPPORTED_IF(!mAllFeaturesSupported);
 
     // D3D12 device must be using DXC to support subgroups feature.
-    DAWN_ASSERT(!mUsingSubgroups || !IsD3D12() || IsDXC());
+    DAWN_ASSERT(!(mUsingSubgroups || mUsingSubgroupsF16) || !IsD3D12() || IsDXC());
 
     wgpu::BufferDescriptor bufferDesc;
     bufferDesc.usage = wgpu::BufferUsage::Storage;
diff --git a/src/dawn/tests/unittests/FeatureTests.cpp b/src/dawn/tests/unittests/FeatureTests.cpp
index 3770ca5..293fcab 100644
--- a/src/dawn/tests/unittests/FeatureTests.cpp
+++ b/src/dawn/tests/unittests/FeatureTests.cpp
@@ -123,7 +123,13 @@
 // For a given feature, returns a set containing the feature and its depending features if any to
 // ensure creating device with these features can success
 std::unordered_set<wgpu::FeatureName> FeatureAndDependenciesSet(wgpu::FeatureName feature) {
-    return {feature};
+    switch (feature) {
+        case wgpu::FeatureName::SubgroupsF16:
+            return {wgpu::FeatureName::SubgroupsF16, wgpu::FeatureName::ShaderF16,
+                    wgpu::FeatureName::Subgroups};
+        default:
+            return {feature};
+    }
 }
 
 bool IsExperimental(wgpu::FeatureName feature) {
diff --git a/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp b/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
index a3f34bd..83114fb 100644
--- a/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
@@ -224,6 +224,62 @@
                           mRequestDeviceCallback.Callback());
 }
 
+// Test that it is an error to request limits with an invalid chained struct
+TEST_F(RequestDeviceValidationTest, InvalidChainedStruct) {
+    wgpu::ChainedStructOut chain = {};
+    wgpu::Limits limits = {};
+    limits.nextInChain = &chain;
+
+    wgpu::DeviceDescriptor descriptor;
+    descriptor.requiredLimits = &limits;
+    EXPECT_CALL(mRequestDeviceCallback,
+                Call(wgpu::RequestDeviceStatus::Error, IsNull(), NonEmptySizedString()))
+        .Times(1);
+    adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                          mRequestDeviceCallback.Callback());
+}
+
+// Test that requiring subroups-f16 feature requires subgroups and shader-f16 features as well
+// TODO(349125474): Decide if this validation is needed, see
+// https://github.com/gpuweb/gpuweb/issues/4734 for detail.
+TEST_F(RequestDeviceValidationTest, SubgroupsF16FeatureDependency) {
+    for (bool requireShaderF16 : {false, true}) {
+        for (bool requireSubgroups : {false, true}) {
+            std::vector<wgpu::FeatureName> features;
+            if (requireShaderF16) {
+                features.push_back(wgpu::FeatureName::ShaderF16);
+            }
+            if (requireSubgroups) {
+                features.push_back(wgpu::FeatureName::Subgroups);
+            }
+            features.push_back(wgpu::FeatureName::SubgroupsF16);
+
+            wgpu::DeviceDescriptor descriptor;
+            descriptor.requiredFeatureCount = features.size();
+            descriptor.requiredFeatures = features.data();
+
+            // Device request with subgroups-f16 feature can only success if shader-f16 feature
+            // and subgroups features are required as well.
+            const bool isSuccess = requireSubgroups && requireShaderF16;
+
+            if (isSuccess) {
+                EXPECT_CALL(mRequestDeviceCallback,
+                            Call(wgpu::RequestDeviceStatus::Success, NotNull(), EmptySizedString()))
+                    .Times(1);
+            } else {
+                EXPECT_CALL(mRequestDeviceCallback,
+                            Call(wgpu::RequestDeviceStatus::Error, IsNull(), NonEmptySizedString()))
+                    .Times(1);
+            }
+
+            EXPECT_DEPRECATION_WARNINGS(
+                adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowSpontaneous,
+                                      mRequestDeviceCallback.Callback()),
+                GetDeviceCreationDeprecationWarningExpectation(descriptor));
+        }
+    }
+}
+
 class DeviceTickValidationTest : public ValidationTest {};
 
 // Device destroy before API-level Tick should always result in no-op and false.
diff --git a/src/dawn/tests/unittests/validation/ShaderModuleValidationTests.cpp b/src/dawn/tests/unittests/validation/ShaderModuleValidationTests.cpp
index 1962f85..8f8658b 100644
--- a/src/dawn/tests/unittests/validation/ShaderModuleValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/ShaderModuleValidationTests.cpp
@@ -822,6 +822,7 @@
     {"clip_distances", false, {"clip-distances"}, {}},
     {"dual_source_blending", false, {"dual-source-blending"}, {}},
     {"subgroups", false, {"subgroups"}, {}},
+    {"subgroups_f16", false, {"shader-f16", "subgroups", "subgroups-f16"}, {"f16", "subgroups"}},
     {"chromium_experimental_pixel_local", true, {"pixel-local-storage-coherent"}, {}},
     {"chromium_disable_uniformity_analysis", true, {}, {}},
     {"chromium_internal_graphite", true, {}, {}},
diff --git a/src/dawn/wire/SupportedFeatures.cpp b/src/dawn/wire/SupportedFeatures.cpp
index 3786b3e..19da897 100644
--- a/src/dawn/wire/SupportedFeatures.cpp
+++ b/src/dawn/wire/SupportedFeatures.cpp
@@ -112,6 +112,7 @@
         case WGPUFeatureName_DawnLoadResolveTexture:
         case WGPUFeatureName_DawnPartialLoadResolveTexture:
         case WGPUFeatureName_Subgroups:
+        case WGPUFeatureName_SubgroupsF16:
         case WGPUFeatureName_ClipDistances:
         case WGPUFeatureName_ChromiumExperimentalImmediateData:
         case WGPUFeatureName_DawnTexelCopyBufferRowAlignment:
diff --git a/src/tint/cmd/bench/BUILD.bazel b/src/tint/cmd/bench/BUILD.bazel
index 816c8a6..358ae92 100644
--- a/src/tint/cmd/bench/BUILD.bazel
+++ b/src/tint/cmd/bench/BUILD.bazel
@@ -99,6 +99,7 @@
     "//src/tint/lang/wgsl/ast",
     "//src/tint/lang/wgsl/program",
     "//src/tint/lang/wgsl/sem",
+    "//src/tint/lang/wgsl:bench",
     "//src/tint/utils",
     "//src/tint/utils/containers",
     "//src/tint/utils/diagnostic",
diff --git a/src/tint/cmd/bench/BUILD.cmake b/src/tint/cmd/bench/BUILD.cmake
index 4dc16d6..470a020 100644
--- a/src/tint/cmd/bench/BUILD.cmake
+++ b/src/tint/cmd/bench/BUILD.cmake
@@ -58,6 +58,7 @@
   tint_lang_wgsl_ast
   tint_lang_wgsl_program
   tint_lang_wgsl_sem
+  tint_lang_wgsl_bench
   tint_utils
   tint_utils_containers
   tint_utils_diagnostic
diff --git a/src/tint/cmd/bench/BUILD.gn b/src/tint/cmd/bench/BUILD.gn
index 35ca535..0594f4a 100644
--- a/src/tint/cmd/bench/BUILD.gn
+++ b/src/tint/cmd/bench/BUILD.gn
@@ -104,6 +104,7 @@
         "${tint_src_dir}/lang/core/constant",
         "${tint_src_dir}/lang/core/type",
         "${tint_src_dir}/lang/wgsl",
+        "${tint_src_dir}/lang/wgsl:bench",
         "${tint_src_dir}/lang/wgsl/ast",
         "${tint_src_dir}/lang/wgsl/program",
         "${tint_src_dir}/lang/wgsl/sem",
diff --git a/src/tint/cmd/bench/extension_bench.cc b/src/tint/cmd/bench/extension_bench.cc
index ed317c7..f56cff5 100644
--- a/src/tint/cmd/bench/extension_bench.cc
+++ b/src/tint/cmd/bench/extension_bench.cc
@@ -122,6 +122,13 @@
         "subgr6u33O",
         "s96grQttupoo",
         "sugro66ps",
+        "ubgrxupszzO166",
+        "suyygroups_f16",
+        "sHHbgroups_Z",
+        "subgroups_f16",
+        "ubgWWou44q_f16",
+        "sOObgroup_f16",
+        "suboruph_Y16",
     };
     for (auto _ : state) {
         for (auto* str : kStrings) {
diff --git a/src/tint/cmd/fuzz/wgsl/dictionary.txt b/src/tint/cmd/fuzz/wgsl/dictionary.txt
index 85d3eef..39ff80a 100644
--- a/src/tint/cmd/fuzz/wgsl/dictionary.txt
+++ b/src/tint/cmd/fuzz/wgsl/dictionary.txt
@@ -421,6 +421,7 @@
 "subgroup_size"
 "subgroup_uniformity"
 "subgroups"
+"subgroups_f16"
 "switch"
 "tan"
 "tanh"
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
index d9caefe..fb7b7df 100644
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
@@ -554,6 +554,7 @@
                                       wgsl::Extension::kF16,
                                       wgsl::Extension::kDualSourceBlending,
                                       wgsl::Extension::kSubgroups,
+                                      wgsl::Extension::kSubgroupsF16,
                                   })) {
         return false;
     }
diff --git a/src/tint/lang/wgsl/BUILD.bazel b/src/tint/lang/wgsl/BUILD.bazel
index ab45c5d..bf28d37 100644
--- a/src/tint/lang/wgsl/BUILD.bazel
+++ b/src/tint/lang/wgsl/BUILD.bazel
@@ -133,6 +133,26 @@
   copts = COPTS,
   visibility = ["//visibility:public"],
 )
+cc_library(
+  name = "bench",
+  alwayslink = True,
+  srcs = [
+    "extension_bench.cc",
+  ],
+  deps = [
+    "//src/tint/lang/wgsl",
+    "//src/tint/utils/containers",
+    "//src/tint/utils/ice",
+    "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/rtti",
+    "@benchmark",
+    "//src/utils",
+  ],
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
 
 alias(
   name = "tint_build_wgsl_reader",
diff --git a/src/tint/lang/wgsl/BUILD.cmake b/src/tint/lang/wgsl/BUILD.cmake
index c4baf01..cfa73cc 100644
--- a/src/tint/lang/wgsl/BUILD.cmake
+++ b/src/tint/lang/wgsl/BUILD.cmake
@@ -143,3 +143,26 @@
     tint_lang_wgsl_writer
   )
 endif(TINT_BUILD_WGSL_WRITER)
+
+################################################################################
+# Target:    tint_lang_wgsl_bench
+# Kind:      bench
+################################################################################
+tint_add_target(tint_lang_wgsl_bench bench
+  lang/wgsl/extension_bench.cc
+)
+
+tint_target_add_dependencies(tint_lang_wgsl_bench bench
+  tint_lang_wgsl
+  tint_utils_containers
+  tint_utils_ice
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_rtti
+)
+
+tint_target_add_external_dependencies(tint_lang_wgsl_bench bench
+  "google-benchmark"
+  "src_utils"
+)
diff --git a/src/tint/lang/wgsl/BUILD.gn b/src/tint/lang/wgsl/BUILD.gn
index 3e27659..d993a9d 100644
--- a/src/tint/lang/wgsl/BUILD.gn
+++ b/src/tint/lang/wgsl/BUILD.gn
@@ -126,3 +126,19 @@
     }
   }
 }
+if (tint_build_benchmarks) {
+  tint_benchmarks_source_set("bench") {
+    sources = [ "extension_bench.cc" ]
+    deps = [
+      "${dawn_root}/src/utils:utils",
+      "${tint_src_dir}:google_benchmark",
+      "${tint_src_dir}/lang/wgsl",
+      "${tint_src_dir}/utils/containers",
+      "${tint_src_dir}/utils/ice",
+      "${tint_src_dir}/utils/macros",
+      "${tint_src_dir}/utils/math",
+      "${tint_src_dir}/utils/memory",
+      "${tint_src_dir}/utils/rtti",
+    ]
+  }
+}
diff --git a/src/tint/lang/wgsl/extension.cc b/src/tint/lang/wgsl/extension.cc
index 06c9696..c3cd9ca 100644
--- a/src/tint/lang/wgsl/extension.cc
+++ b/src/tint/lang/wgsl/extension.cc
@@ -75,6 +75,9 @@
     if (str == "subgroups") {
         return Extension::kSubgroups;
     }
+    if (str == "subgroups_f16") {
+        return Extension::kSubgroupsF16;
+    }
     return Extension::kUndefined;
 }
 
@@ -104,6 +107,8 @@
             return "f16";
         case Extension::kSubgroups:
             return "subgroups";
+        case Extension::kSubgroupsF16:
+            return "subgroups_f16";
     }
     return "<unknown>";
 }
diff --git a/src/tint/lang/wgsl/extension.h b/src/tint/lang/wgsl/extension.h
index 46f6d3c..771bfc5 100644
--- a/src/tint/lang/wgsl/extension.h
+++ b/src/tint/lang/wgsl/extension.h
@@ -57,6 +57,7 @@
     kDualSourceBlending,
     kF16,
     kSubgroups,
+    kSubgroupsF16,
 };
 
 /// @param value the enum value
@@ -88,6 +89,7 @@
     "dual_source_blending",
     "f16",
     "subgroups",
+    "subgroups_f16",
 };
 
 /// All extensions
@@ -103,6 +105,7 @@
     Extension::kDualSourceBlending,
     Extension::kF16,
     Extension::kSubgroups,
+    Extension::kSubgroupsF16,
 };
 
 /// A unique vector of extensions
diff --git a/src/tint/lang/wgsl/extension_bench.cc b/src/tint/lang/wgsl/extension_bench.cc
new file mode 100644
index 0000000..2f8861d
--- /dev/null
+++ b/src/tint/lang/wgsl/extension_bench.cc
@@ -0,0 +1,151 @@
+// Copyright 2022 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by 'tools/src/cmd/gen' using the template:
+//   src/tint/lang/wgsl/extension_bench.cc.tmpl
+//
+// To regenerate run: './tools/run gen'
+//
+//                       Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/lang/wgsl/extension.h"
+
+#include <array>
+
+#include "benchmark/benchmark.h"
+
+namespace tint::wgsl {
+namespace {
+
+void ExtensionParser(::benchmark::State& state) {
+    const char* kStrings[] = {
+        "chromium_disableuniformiccy_analysis",
+        "chromil3_disable_unifority_analss",
+        "chromium_disable_Vniformity_analysis",
+        "chromium_disable_uniformity_analysis",
+        "chromium_dis1ble_uniformity_analysis",
+        "chromium_qqisable_unifomity_anaJysis",
+        "chrollium_disable_uniformity_analysi77",
+        "cqqromium_eppperimental_framebuffe_fetcHH",
+        "chrmium_experimvntal_frcmebufer_ftch",
+        "chromium_expebimental_framGbufer_fetch",
+        "chromium_experimental_framebuffer_fetch",
+        "chromium_experimental_vramebuffeii_fetch",
+        "chro8WWum_experimental_framebuffer_fetch",
+        "chromium_eperimenxxMl_framebuffer_fetch",
+        "chromum_experimental_pixeX_loggal",
+        "chromium_expVrXmntal_ixel_local",
+        "3hromium_experimental_pixel_local",
+        "chromium_experimental_pixel_local",
+        "chromium_eEperimental_pixel_local",
+        "chTTomiu_experimentaPP_pixel_local",
+        "cxxromium_expddrimenal_pixel_local",
+        "c44romium_experimental_push_constant",
+        "chromium_experimental_pSSsVV_constant",
+        "chrom22Rm_experimental_pushRonstant",
+        "chromium_experimental_push_constant",
+        "chromium_exp9rimFntal_ush_constant",
+        "chrmium_experimental_push_constant",
+        "cOOromium_experiVeHtal_puh_conRRtant",
+        "chromium_eyperimental_subgoup_matrix",
+        "Ghromium_experrim77nllal_subgnnoup_matrix",
+        "ch4omium_experimental00subgroup_matrix",
+        "chromium_experimental_subgroup_matrix",
+        "chromium_exeimoontal_subgroup_atrix",
+        "chromium_experimnal_subgroup_mazzrix",
+        "chro11ium_experienial_subgrppup_matrix",
+        "chXXomium_internal_graphite",
+        "chromi55m_internnal_gra99hiIIe",
+        "chSSomiuY_internal_aarHHphrrte",
+        "chromium_internal_graphite",
+        "kkhromium_nternal_rahHte",
+        "chromium_nRegnaj_graphite",
+        "chromium_ntebnal_gaphite",
+        "chromium_internal_input_atjachments",
+        "chromium_internal_inpt_attachments",
+        "chromium_nteral_iqput_attachments",
+        "chromium_internal_input_attachments",
+        "chromium_internal_input_aNNtachents",
+        "chromium_internalinpt_attavvhments",
+        "chromium_internal_inut_attacQQments",
+        "chromirm_intenal_rfflaxed_unifrm_layout",
+        "chromium_internal_jelaxed_uniform_layout",
+        "chromium_interna_relNNxed_uwwiform_lay82t",
+        "chromium_internal_relaxed_uniform_layout",
+        "chromium_internal_relaxed_uniform_layut",
+        "chromium_internal_relaxed_rrniform_layout",
+        "chromium_internal_relaxedGuniform_layout",
+        "clip_distanceFF",
+        "cEipdtances",
+        "cli_rristances",
+        "clip_distances",
+        "lip_distanes",
+        "DXp_diJJtances",
+        "cl8pdistane",
+        "dul_okrc_blen11ing",
+        "dua_source_blending",
+        "duJl_source_blendig",
+        "dual_source_blending",
+        "dual_source_clending",
+        "dual_sOurce_blending",
+        "dualKKs__urce_blttvnding",
+        "xx8",
+        "__F",
+        "f1q",
+        "f16",
+        "331O",
+        "ftt6QQ",
+        "666",
+        "zzxbO6rops",
+        "subgyyoups",
+        "HHugroZs",
+        "subgroups",
+        "sWW44roupq",
+        "sOObgoups",
+        "sbgroYps",
+        "subroups_f",
+        "suFgoups_f16",
+        "subgowps_f16",
+        "subgroups_f16",
+        "suffgKups_f6",
+        "KKubgroqps_f16",
+        "subFroup3mmf16",
+    };
+    for (auto _ : state) {
+        for (auto* str : kStrings) {
+            auto result = ParseExtension(str);
+            benchmark::DoNotOptimize(result);
+        }
+    }
+}  // NOLINT(readability/fn_size)
+
+BENCHMARK(ExtensionParser);
+
+}  // namespace
+}  // namespace tint::wgsl
diff --git a/src/tint/lang/wgsl/extension_test.cc b/src/tint/lang/wgsl/extension_test.cc
index 9434d4f..7bc4fa3 100644
--- a/src/tint/lang/wgsl/extension_test.cc
+++ b/src/tint/lang/wgsl/extension_test.cc
@@ -68,6 +68,7 @@
     {"dual_source_blending", Extension::kDualSourceBlending},
     {"f16", Extension::kF16},
     {"subgroups", Extension::kSubgroups},
+    {"subgroups_f16", Extension::kSubgroupsF16},
 };
 
 static constexpr Case kInvalidCases[] = {
@@ -104,6 +105,9 @@
     {"skkkgroups", Extension::kUndefined},
     {"siibgrop", Extension::kUndefined},
     {"subgroupXX", Extension::kUndefined},
+    {"subII9rnn55ps_f16", Extension::kUndefined},
+    {"YubHHrouaas_SSr16", Extension::kUndefined},
+    {"subgupkkHf1", Extension::kUndefined},
 };
 
 using ExtensionParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc b/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
index 5c72205..8080b15 100644
--- a/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
+++ b/src/tint/lang/wgsl/reader/parser/enable_directive_test.cc
@@ -175,7 +175,7 @@
     // Error when unknown extension found
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups')");
+Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups', 'subgroups_f16')");
     auto program = p->program();
     auto& ast = program.AST();
     EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -189,7 +189,7 @@
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), R"(1:8: expected extension
 Did you mean 'f16'?
-Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups')");
+Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups', 'subgroups_f16')");
     auto program = p->program();
     auto& ast = program.AST();
     EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -203,7 +203,7 @@
     // Error when unknown extension found
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_framebuffer_fetch', 'chromium_experimental_pixel_local', 'chromium_experimental_push_constant', 'chromium_experimental_subgroup_matrix', 'chromium_internal_graphite', 'chromium_internal_input_attachments', 'clip_distances', 'dual_source_blending', 'f16', 'subgroups')");
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_framebuffer_fetch', 'chromium_experimental_pixel_local', 'chromium_experimental_push_constant', 'chromium_experimental_subgroup_matrix', 'chromium_internal_graphite', 'chromium_internal_input_attachments', 'clip_distances', 'dual_source_blending', 'f16', 'subgroups', 'subgroups_f16')");
     auto program = p->program();
     auto& ast = program.AST();
     EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -251,7 +251,7 @@
         p->translation_unit();
         EXPECT_TRUE(p->has_error());
         EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups')");
+Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups', 'subgroups_f16')");
         auto program = p->program();
         auto& ast = program.AST();
         EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -262,7 +262,7 @@
         p->translation_unit();
         EXPECT_TRUE(p->has_error());
         EXPECT_EQ(p->error(), R"(1:8: expected extension
-Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups')");
+Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups', 'subgroups_f16')");
         auto program = p->program();
         auto& ast = program.AST();
         EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -274,7 +274,7 @@
         EXPECT_TRUE(p->has_error());
         EXPECT_EQ(p->error(), R"(1:8: expected extension
 Did you mean 'f16'?
-Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups')");
+Possible values: 'clip_distances', 'dual_source_blending', 'f16', 'subgroups', 'subgroups_f16')");
         auto program = p->program();
         auto& ast = program.AST();
         EXPECT_EQ(ast.Enables().Length(), 0u);
diff --git a/src/tint/lang/wgsl/resolver/builtin_validation_test.cc b/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
index 9b82b58..0b603a0 100644
--- a/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/builtin_validation_test.cc
@@ -987,7 +987,7 @@
     EXPECT_TRUE(r()->Resolve());
 }
 
-TEST_F(ResolverBuiltinValidationTest, SubgroupBroadcastWithExtension_F16) {
+TEST_F(ResolverBuiltinValidationTest, SubgroupBroadcastWithoutSubgroupsF16Extension_F16) {
     // enable f16;
     // enable subgroups;
     // fn func -> f16 { return subgroupBroadcast(1.h,0); }
@@ -1016,6 +1016,7 @@
 }
 
 TEST_F(ResolverBuiltinValidationTest, SubgroupBroadcastWithoutShaderF16Extension_F16) {
+    // enable f16;
     // fn func -> f16 { return subgroupBroadcast(1.h,0); }
     Enable(wgsl::Extension::kSubgroups);
     Func("func", tint::Empty, ty.f16(),
@@ -1026,6 +1027,39 @@
     EXPECT_EQ(r()->error(), R"(error: 'f16' type used without 'f16' extension enabled)");
 }
 
+TEST_F(ResolverBuiltinValidationTest, SubgroupBroadcastWithSubgroupsF16WithoutShaderF16Extension) {
+    // enable f16;
+    // fn func -> f16 { return subgroupBroadcast(1.h,0); }
+    Enable(wgsl::Extension::kSubgroups);
+    Enable(wgsl::Extension::kSubgroupsF16);
+    Func("func", tint::Empty, ty.f16(),
+         Vector{
+             Return(Call(Source{{12, 34}}, "subgroupBroadcast", 1_h, 0_u)),
+         });
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(error: 'f16' type used without 'f16' extension enabled
+error: extension 'subgroups_f16' cannot be used without extension 'f16')");
+}
+
+TEST_F(ResolverBuiltinValidationTest, SubgroupBroadcastWithExtensions_F16) {
+    // enable f16;
+    // enable subgroups;
+    // enable subgroups_f16;
+    // fn func -> f16 { return subgroupBroadcast(1.h,0); }
+    Enable(wgsl::Extension::kF16);
+    Enable(wgsl::Extension::kSubgroups);
+    // TODO(crbug.com/380244620): Remove when 'subgroups_f16' has been fully deprecated.
+    Enable(wgsl::Extension::kSubgroupsF16);
+
+    Func("func", tint::Empty, ty.f16(),
+         Vector{
+             Return(Call("subgroupBroadcast", 1_h, 0_u)),
+         });
+
+    EXPECT_TRUE(r()->Resolve());
+}
+
 TEST_F(ResolverBuiltinValidationTest, SubgroupBroadcastWithoutExtension_VecF16) {
     // enable f16;
     // enable subgroups;
@@ -1040,12 +1074,15 @@
     EXPECT_TRUE(r()->Resolve());
 }
 
-TEST_F(ResolverBuiltinValidationTest, SubgroupBroadcastWithExtension_VecF16) {
+TEST_F(ResolverBuiltinValidationTest, SubgroupBroadcastWithExtensions_VecF16) {
     // enable f16;
     // enable subgroups;
+    // enable subgroups_f16;
     // fn func -> vec4<f16> { return subgroupBroadcast(vec4(1.h),0); }
     Enable(wgsl::Extension::kF16);
     Enable(wgsl::Extension::kSubgroups);
+    // TODO(crbug.com/380244620): Remove when 'subgroups_f16' has been fully deprecated.
+    Enable(wgsl::Extension::kSubgroupsF16);
 
     Func("func", tint::Empty, ty.vec4<f16>(),
          Vector{
diff --git a/src/tint/lang/wgsl/resolver/subgroups_extension_test.cc b/src/tint/lang/wgsl/resolver/subgroups_extension_test.cc
index 440633f..3fa8e4d 100644
--- a/src/tint/lang/wgsl/resolver/subgroups_extension_test.cc
+++ b/src/tint/lang/wgsl/resolver/subgroups_extension_test.cc
@@ -38,6 +38,26 @@
 
 using ResolverSubgroupsExtensionTest = ResolverTest;
 
+// Enabling subgroups_f16 without enabling subgroups should fail.
+TEST_F(ResolverSubgroupsExtensionTest, UseSubgroupsF16WithoutSubgroups) {
+    Enable(wgsl::Extension::kF16);
+    Enable(wgsl::Extension::kSubgroupsF16);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(error: extension 'subgroups_f16' cannot be used without extension 'subgroups')");
+}
+
+// Enabling subgroups_f16 without enabling f16 should fail.
+TEST_F(ResolverSubgroupsExtensionTest, UseSubgroupsF16WithoutF16) {
+    Enable(wgsl::Extension::kSubgroups);
+    Enable(wgsl::Extension::kSubgroupsF16);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(error: extension 'subgroups_f16' cannot be used without extension 'f16')");
+}
+
 // Using a subgroup_size builtin attribute without subgroups enabled should fail.
 TEST_F(ResolverSubgroupsExtensionTest, UseSubgroupSizeAttribWithoutExtensionError) {
     Structure("Inputs",
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index eaa3576..8a05a68 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -286,6 +286,21 @@
         }
     }
 
+    if (enabled_extensions_.Contains(wgsl::Extension::kSubgroupsF16)) {
+        if (!enabled_extensions_.Contains(wgsl::Extension::kSubgroups)) {
+            AddError(source_of(wgsl::Extension::kSubgroupsF16))
+                << "extension " << style::Code("subgroups_f16")
+                << " cannot be used without extension " << style::Code("subgroups");
+            return false;
+        }
+        if (!enabled_extensions_.Contains(wgsl::Extension::kF16)) {
+            AddError(source_of(wgsl::Extension::kSubgroupsF16))
+                << "extension " << style::Code("subgroups_f16")
+                << " cannot be used without extension " << style::Code("f16");
+            return false;
+        }
+    }
+
     return true;
 }
 
diff --git a/src/tint/lang/wgsl/wgsl.def b/src/tint/lang/wgsl/wgsl.def
index 2bd1e29..257c4b0 100644
--- a/src/tint/lang/wgsl/wgsl.def
+++ b/src/tint/lang/wgsl/wgsl.def
@@ -79,6 +79,8 @@
   dual_source_blending
   // WGSL Extension "subgroups"
   subgroups
+  // WGSL Extension "subgroups_f16"
+  subgroups_f16
 
   // A Chromium-specific extension for disabling uniformity analysis.
   chromium_disable_uniformity_analysis
diff --git a/test/tint/builtins/gen/gen.wgsl.tmpl b/test/tint/builtins/gen/gen.wgsl.tmpl
index 798f45d..34ff24a 100644
--- a/test/tint/builtins/gen/gen.wgsl.tmpl
+++ b/test/tint/builtins/gen/gen.wgsl.tmpl
@@ -297,7 +297,7 @@
 {{- /* Emit 'enable chromium_experimental_subgroup_matrix' if required */ -}}
 {{-   if (HasPrefix $builtin_name "subgroupMatrix")}}
 enable chromium_experimental_subgroup_matrix;
-{{/* Emit 'enable subgroups' if required */ -}}
+{{/* Emit 'enable subgroups' and 'enable subgroups_f16' if required */ -}}
 {{-   else if or (HasPrefix $builtin_name "subgroup") (HasPrefix $builtin_name "quad")}}
 enable subgroups;
 {{    end -}}