Repository: KhronosGroup/SPIRV-Tools (language: python)

Issue:
opt: add GroupNonUniformPartitionedNV capability to trim pass



Discussion / hints:
Will be used for the implementation for https://github.com/microsoft/DirectXShaderCompiler/issues/6545
The instructions enabled by this capability aren't and won't be enabled by any others (they have the NV suffix on the instruction too), but that is an optimization I looked into for `SPV_KHR_ray_query` and `SPV_KHR_ray_tracing` in the past, since they implement some of the same instructions and capabilities. It might be worth looking into if we find other examples or the hardware overlap gets more divergent.
Rebasing to see if that makes the bots happy
> is an optimization I looked into for `SPV_KHR_ray_query` and `SPV_KHR_ray_tracing` in the past, since they implement some of the same instructions and capabilities. It might be worth look

What was the failure mode?  
Once this landed on main, the continuous integration bots failed on mac bazel, due to 60s timeouts, it looks like:

//:opt_fold_test                                                        TIMEOUT in 60.2s
//:val_builtins_test                                                    TIMEOUT in 60.2s
@dneto0 The hypothetical optimization of when to apply `SPV_KHR_ray_query` and `SPV_KHR_ray_tracing` is a tangent and not related to this change. 

The only effect of this PR should be that the capability trimming pass now removes `GroupNonUniformPartitionedNV` when it's not needed, so I can't imagine it would cause bot timeouts, but if it does appear to be the culprit please feel free to revert.
Oh and the bot failure I saw before rebase was just for an unrelated file's clang-format failure.

Fix patch:
diff --git a/source/opt/trim_capabilities_pass.cpp b/source/opt/trim_capabilities_pass.cpp
index 24f9e4670e..aaf4d322a8 100644
--- a/source/opt/trim_capabilities_pass.cpp
+++ b/source/opt/trim_capabilities_pass.cpp
@@ -399,6 +399,33 @@ ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities,
 
   return output;
 }
+
+bool hasOpcodeConflictingCapabilities(spv::Op opcode) {
+  switch (opcode) {
+    case spv::Op::OpBeginInvocationInterlockEXT:
+    case spv::Op::OpEndInvocationInterlockEXT:
+    case spv::Op::OpGroupNonUniformIAdd:
+    case spv::Op::OpGroupNonUniformFAdd:
+    case spv::Op::OpGroupNonUniformIMul:
+    case spv::Op::OpGroupNonUniformFMul:
+    case spv::Op::OpGroupNonUniformSMin:
+    case spv::Op::OpGroupNonUniformUMin:
+    case spv::Op::OpGroupNonUniformFMin:
+    case spv::Op::OpGroupNonUniformSMax:
+    case spv::Op::OpGroupNonUniformUMax:
+    case spv::Op::OpGroupNonUniformFMax:
+    case spv::Op::OpGroupNonUniformBitwiseAnd:
+    case spv::Op::OpGroupNonUniformBitwiseOr:
+    case spv::Op::OpGroupNonUniformBitwiseXor:
+    case spv::Op::OpGroupNonUniformLogicalAnd:
+    case spv::Op::OpGroupNonUniformLogicalOr:
+    case spv::Op::OpGroupNonUniformLogicalXor:
+      return true;
+    default:
+      return false;
+  }
+}
+
 }  // namespace
 
 TrimCapabilitiesPass::TrimCapabilitiesPass()
@@ -416,10 +443,7 @@ TrimCapabilitiesPass::TrimCapabilitiesPass()
 void TrimCapabilitiesPass::addInstructionRequirementsForOpcode(
     spv::Op opcode, CapabilitySet* capabilities,
     ExtensionSet* extensions) const {
-  // Ignoring OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT
-  // because they have three possible capabilities, only one of which is needed
-  if (opcode == spv::Op::OpBeginInvocationInterlockEXT ||
-      opcode == spv::Op::OpEndInvocationInterlockEXT) {
+  if (hasOpcodeConflictingCapabilities(opcode)) {
     return;
   }
 
diff --git a/source/opt/trim_capabilities_pass.h b/source/opt/trim_capabilities_pass.h
index 81c07b8227..3ff6dba2d2 100644
--- a/source/opt/trim_capabilities_pass.h
+++ b/source/opt/trim_capabilities_pass.h
@@ -81,6 +81,11 @@ class TrimCapabilitiesPass : public Pass {
       spv::Capability::FragmentShaderPixelInterlockEXT,
       spv::Capability::FragmentShaderSampleInterlockEXT,
       spv::Capability::FragmentShaderShadingRateInterlockEXT,
+      spv::Capability::GroupNonUniform,
+      spv::Capability::GroupNonUniformArithmetic,
+      spv::Capability::GroupNonUniformClustered,
+      spv::Capability::GroupNonUniformPartitionedNV,
+      spv::Capability::GroupNonUniformVote,
       spv::Capability::Groups,
       spv::Capability::ImageMSArray,
       spv::Capability::Int16,
@@ -99,7 +104,6 @@ class TrimCapabilitiesPass : public Pass {
       spv::Capability::StorageUniform16,
       spv::Capability::StorageUniformBufferBlock16,
       spv::Capability::VulkanMemoryModelDeviceScope,
-      spv::Capability::GroupNonUniformPartitionedNV
       // clang-format on
   };
 


Test patch:
diff --git a/test/opt/trim_capabilities_pass_test.cpp b/test/opt/trim_capabilities_pass_test.cpp
index d74ccdf2f8..9b2d767d59 100644
--- a/test/opt/trim_capabilities_pass_test.cpp
+++ b/test/opt/trim_capabilities_pass_test.cpp
@@ -2660,6 +2660,505 @@ TEST_F(TrimCapabilitiesPassTest,
   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
 }
 
+TEST_F(TrimCapabilitiesPassTest, GroupNonUniform_RemovedWhenUnused) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability GroupNonUniformVote
+; CHECK-NOT:   OpCapability GroupNonUniformVote
+               OpCapability GroupNonUniformArithmetic
+; CHECK-NOT:   OpCapability GroupNonUniformArithmetic
+               OpCapability GroupNonUniformClustered
+; CHECK-NOT:   OpCapability GroupNonUniformClustered
+               OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:   OpCapability GroupNonUniformPartitionedNV
+               OpCapability GroupNonUniform
+; CHECK-NOT:   OpCapability GroupNonUniform
+               OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:   OpExtension "SPV_NV_shader_subgroup_partitioned"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 2 4
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       GroupNonUniform_RemainsGroupNonUniformWhenInUse) {
+  const std::string kTest = R"(
+                   OpCapability GroupNonUniformVote
+; CHECK-NOT:       OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK-NOT:       OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK:           OpCapability GroupNonUniform
+                   OpCapability Shader
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+ %scope_subgroup = OpConstant %uint 3
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = OpGroupNonUniformElect %bool %scope_subgroup
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       GroupNonUniformVote_Remains_OpGroupNonUniformAll) {
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK:           OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK-NOT:       OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+ %scope_subgroup = OpConstant %uint 3
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = OpGroupNonUniformAll %bool %scope_subgroup %true
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       GroupNonUniformVote_Remains_OpGroupNonUniformAny) {
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK:           OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK-NOT:       OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+ %scope_subgroup = OpConstant %uint 3
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = OpGroupNonUniformAny %bool %scope_subgroup %true
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       GroupNonUniformArithmetic_Remains_OpGroupNonUniformIAdd_Reduce) {
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK-NOT:       OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK:           OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK-NOT:       OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+ %scope_subgroup = OpConstant %uint 3
+         %uint_1 = OpConstant %uint 1
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = OpGroupNonUniformIAdd %uint %scope_subgroup Reduce %uint_1
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       GroupNonUniformArithmetic_Remains_OpGroupNonUniformIAdd_InclusiveScan) {
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK-NOT:       OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK:           OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK-NOT:       OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+ %scope_subgroup = OpConstant %uint 3
+         %uint_1 = OpConstant %uint 1
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = OpGroupNonUniformIAdd %uint %scope_subgroup InclusiveScan %uint_1
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       GroupNonUniformArithmetic_Remains_OpGroupNonUniformIAdd_ExclusiveScan) {
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK-NOT:       OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK:           OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK-NOT:       OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+ %scope_subgroup = OpConstant %uint 3
+         %uint_1 = OpConstant %uint 1
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = OpGroupNonUniformIAdd %uint %scope_subgroup ExclusiveScan %uint_1
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       GroupNonUniformClustered_Remains_OpGroupNonUniformIAdd_ClusteredReduce) {
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK-NOT:       OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK:           OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+ %scope_subgroup = OpConstant %uint 3
+         %uint_1 = OpConstant %uint 1
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = OpGroupNonUniformIAdd %uint %scope_subgroup ClusteredReduce %uint_1 %uint_1
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+struct SubgroupTestCase {
+  // The result type of the subgroup instruction.
+  std::string resultType;
+  // The opcode of the subgroup instruction.
+  std::string opcode;
+  // The actual operand of the subgroup instruction.
+  std::string operand;
+};
+
+static const std::vector<SubgroupTestCase> kSubgroupTestCases{
+    // clang-format off
+  { "uint",  "OpGroupNonUniformIAdd",       "uint_1"  },
+  { "float", "OpGroupNonUniformFAdd",       "float_1" },
+  { "uint",  "OpGroupNonUniformIMul",       "uint_1"  },
+  { "float", "OpGroupNonUniformFMul",       "float_1" },
+  { "int",   "OpGroupNonUniformSMin",       "int_1"   },
+  { "uint",  "OpGroupNonUniformUMin",       "uint_1"  },
+  { "float", "OpGroupNonUniformFMin",       "float_1" },
+  { "int",   "OpGroupNonUniformSMax",       "int_1"   },
+  { "uint",  "OpGroupNonUniformUMax",       "uint_1"  },
+  { "float", "OpGroupNonUniformFMax",       "float_1" },
+  { "uint",  "OpGroupNonUniformBitwiseAnd", "uint_1"  },
+  { "uint",  "OpGroupNonUniformBitwiseOr",  "uint_1"  },
+  { "uint",  "OpGroupNonUniformBitwiseXor", "uint_1"  },
+  { "bool",  "OpGroupNonUniformLogicalAnd", "true"    },
+  { "bool",  "OpGroupNonUniformLogicalOr",  "true"    },
+  { "bool",  "OpGroupNonUniformLogicalXor", "true"    }
+    // clang-format on
+};
+
+using TrimCapabilitiesPassTestSubgroupNV_Unsigned = PassTest<
+    ::testing::TestWithParam<std::tuple<SubgroupTestCase, std::string>>>;
+TEST_P(TrimCapabilitiesPassTestSubgroupNV_Unsigned,
+       GroupNonUniformPartitionedNV_Remains) {
+  SubgroupTestCase test_case = std::get<0>(GetParam());
+  const std::string operation = std::get<1>(GetParam());
+
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK-NOT:       OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK-NOT:       OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK:           OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK:           OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+            %int = OpTypeInt 32 1
+          %float = OpTypeFloat 32
+         %v4uint = OpTypeVector %uint 4
+ %scope_subgroup = OpConstant %uint 3
+         %uint_1 = OpConstant %uint 1
+          %int_1 = OpConstant %int 1
+        %float_1 = OpConstant %float 1
+     %uint4_1111 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = )" + test_case.opcode +
+                            " %" + test_case.resultType + " %scope_subgroup " +
+                            operation + " %" + test_case.operand +
+                            R"( %uint4_1111
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TrimCapabilitiesPassTestSubgroupNV_Unsigned_I,
+    TrimCapabilitiesPassTestSubgroupNV_Unsigned,
+    ::testing::Combine(::testing::ValuesIn(kSubgroupTestCases),
+                       ::testing::Values("PartitionedReduceNV",
+                                         "PartitionedInclusiveScanNV",
+                                         "PartitionedExclusiveScanNV")),
+    [](const ::testing::TestParamInfo<
+        TrimCapabilitiesPassTestSubgroupNV_Unsigned::ParamType>& info) {
+      return std::get<0>(info.param).opcode + "_" + std::get<1>(info.param);
+    });
+
+using TrimCapabilitiesPassTestSubgroupArithmetic_Unsigned = PassTest<
+    ::testing::TestWithParam<std::tuple<SubgroupTestCase, std::string>>>;
+TEST_P(TrimCapabilitiesPassTestSubgroupArithmetic_Unsigned,
+       GroupNonUniformPartitionedArithmetic_Remains) {
+  SubgroupTestCase test_case = std::get<0>(GetParam());
+  const std::string operation = std::get<1>(GetParam());
+
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK-NOT:       OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK:           OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK-NOT:       OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+            %int = OpTypeInt 32 1
+          %float = OpTypeFloat 32
+         %v4uint = OpTypeVector %uint 4
+ %scope_subgroup = OpConstant %uint 3
+         %uint_1 = OpConstant %uint 1
+          %int_1 = OpConstant %int 1
+        %float_1 = OpConstant %float 1
+     %uint4_1111 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = )" + test_case.opcode +
+                            " %" + test_case.resultType + " %scope_subgroup " +
+                            operation + " %" + test_case.operand + R"( %uint_1
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TrimCapabilitiesPassTestSubgroupArithmetic_Unsigned_I,
+    TrimCapabilitiesPassTestSubgroupArithmetic_Unsigned,
+    ::testing::Combine(::testing::ValuesIn(kSubgroupTestCases),
+                       ::testing::Values("Reduce", "InclusiveScan",
+                                         "ExclusiveScan")),
+    [](const ::testing::TestParamInfo<
+        TrimCapabilitiesPassTestSubgroupArithmetic_Unsigned::ParamType>& info) {
+      return std::get<0>(info.param).opcode + "_" + std::get<1>(info.param);
+    });
+
+using TrimCapabilitiesPassTestSubgroupClustered_Unsigned = PassTest<
+    ::testing::TestWithParam<std::tuple<SubgroupTestCase, std::string>>>;
+TEST_P(TrimCapabilitiesPassTestSubgroupClustered_Unsigned,
+       GroupNonUniformPartitionedClustered_Remains) {
+  SubgroupTestCase test_case = std::get<0>(GetParam());
+  const std::string operation = std::get<1>(GetParam());
+
+  const std::string kTest = R"(
+                   OpCapability Shader
+                   OpCapability GroupNonUniformVote
+; CHECK-NOT:       OpCapability GroupNonUniformVote
+                   OpCapability GroupNonUniformArithmetic
+; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
+                   OpCapability GroupNonUniformClustered
+; CHECK:           OpCapability GroupNonUniformClustered
+                   OpCapability GroupNonUniformPartitionedNV
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniform
+; CHECK-NOT:       OpCapability GroupNonUniform
+                   OpExtension "SPV_NV_shader_subgroup_partitioned"
+; CHECK-NOT:       OpExtension "SPV_NV_shader_subgroup_partitioned"
+                   OpMemoryModel Logical GLSL450
+                   OpEntryPoint GLCompute %main "main"
+                   OpExecutionMode %main LocalSize 1 2 4
+           %void = OpTypeVoid
+           %bool = OpTypeBool
+           %uint = OpTypeInt 32 0
+            %int = OpTypeInt 32 1
+          %float = OpTypeFloat 32
+         %v4uint = OpTypeVector %uint 4
+ %scope_subgroup = OpConstant %uint 3
+         %uint_1 = OpConstant %uint 1
+          %int_1 = OpConstant %int 1
+        %float_1 = OpConstant %float 1
+     %uint4_1111 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1
+           %true = OpConstantTrue %bool
+              %3 = OpTypeFunction %void
+           %main = OpFunction %void None %3
+              %6 = OpLabel
+              %7 = )" + test_case.opcode +
+                            " %" + test_case.resultType + " %scope_subgroup " +
+                            operation + " %" + test_case.operand + R"( %uint_1
+                   OpReturn
+                   OpFunctionEnd;
+  )";
+  const auto result = SinglePassRunAndMatch<TrimCapabilitiesPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TrimCapabilitiesPassTestSubgroupClustered_Unsigned_I,
+    TrimCapabilitiesPassTestSubgroupClustered_Unsigned,
+    ::testing::Combine(::testing::ValuesIn(kSubgroupTestCases),
+                       ::testing::Values("ClusteredReduce")),
+    [](const ::testing::TestParamInfo<
+        TrimCapabilitiesPassTestSubgroupClustered_Unsigned::ParamType>& info) {
+      return std::get<0>(info.param).opcode + "_" + std::get<1>(info.param);
+    });
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools