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

Issue:
Strip unused capabilities (i.e. Float64, PhysicalStorageBuffer)
It has come to our attention that DXC likes to emit SPIR-V capabilities even if the code using them is not reachable, for example `Float64` being declared here even though nobody is using the `static double` we declared
https://godbolt.org/z/ca5M318GE

The reason we're filing this against SPIRV-Tools and not DXC is because we know DXC uses spv-opt to legalize the SPIR-V it emits, and we believe that the legalization should strip unused extensions and capabilities.


Discussion / hints:
A capabilities trimming pass has been added recently: https://github.com/KhronosGroup/SPIRV-Tools/blob/main/source/opt/trim_capabilities_pass.cpp. This is now automatically called by DXC, and capabilities are being added on a case by case basis.
thats really cool, thank you!
I'd still request PhysicalStorageBuffer pointers (BDA) trimming cause it tends to disable/break Renderdoc's source-level debugging 

Fix patch:
diff --git a/source/opt/trim_capabilities_pass.h b/source/opt/trim_capabilities_pass.h
index 84eff6ef76..73d5dc80d5 100644
--- a/source/opt/trim_capabilities_pass.h
+++ b/source/opt/trim_capabilities_pass.h
@@ -74,14 +74,18 @@ class TrimCapabilitiesPass : public Pass {
   // contains unsupported instruction, the pass could yield bad results.
   static constexpr std::array kSupportedCapabilities{
       // clang-format off
+      spv::Capability::ComputeDerivativeGroupLinearNV,
+      spv::Capability::ComputeDerivativeGroupQuadsNV,
       spv::Capability::Float64,
       spv::Capability::FragmentShaderPixelInterlockEXT,
       spv::Capability::FragmentShaderSampleInterlockEXT,
       spv::Capability::FragmentShaderShadingRateInterlockEXT,
       spv::Capability::Groups,
+      spv::Capability::ImageMSArray,
       spv::Capability::Int64,
       spv::Capability::Linkage,
       spv::Capability::MinLod,
+      spv::Capability::PhysicalStorageBufferAddresses,
       spv::Capability::RayQueryKHR,
       spv::Capability::RayTracingKHR,
       spv::Capability::RayTraversalPrimitiveCullingKHR,
@@ -91,10 +95,7 @@ class TrimCapabilitiesPass : public Pass {
       spv::Capability::StorageInputOutput16,
       spv::Capability::StoragePushConstant16,
       spv::Capability::StorageUniform16,
-      spv::Capability::StorageUniformBufferBlock16,
-      spv::Capability::ImageMSArray,
-      spv::Capability::ComputeDerivativeGroupQuadsNV,
-      spv::Capability::ComputeDerivativeGroupLinearNV
+      spv::Capability::StorageUniformBufferBlock16
       // clang-format on
   };
 


Test patch:
diff --git a/test/opt/trim_capabilities_pass_test.cpp b/test/opt/trim_capabilities_pass_test.cpp
index 5636d5f933..14a8aa3a56 100644
--- a/test/opt/trim_capabilities_pass_test.cpp
+++ b/test/opt/trim_capabilities_pass_test.cpp
@@ -2366,6 +2366,126 @@ TEST_F(TrimCapabilitiesPassTest,
   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
 }
 
+TEST_F(TrimCapabilitiesPassTest, PhysicalStorageBuffer_RemovedWhenUnused) {
+  const std::string kTest = R"(
+               OpCapability PhysicalStorageBufferAddresses
+; CHECK-NOT:   OpCapability PhysicalStorageBufferAddresses
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       PhysicalStorageBuffer_RemainsWithOpTypeForwardPointer) {
+  const std::string kTest = R"(
+               OpCapability PhysicalStorageBufferAddresses
+; CHECK:       OpCapability PhysicalStorageBufferAddresses
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 2 4
+       %void = OpTypeVoid
+        %int = OpTypeInt 32 0
+     %struct = OpTypeStruct %int
+               OpTypeForwardPointer %ptr PhysicalStorageBuffer
+        %ptr = OpTypePointer PhysicalStorageBuffer %struct
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       PhysicalStorageBuffer_RemainsWithPhysicalStorageBufferStorage) {
+  const std::string kTest = R"(
+               OpCapability PhysicalStorageBufferAddresses
+; CHECK:       OpCapability PhysicalStorageBufferAddresses
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 2 4
+       %void = OpTypeVoid
+        %int = OpTypeInt 32 0
+     %struct = OpTypeStruct %int
+        %ptr = OpTypePointer PhysicalStorageBuffer %struct
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       PhysicalStorageBuffer_RemainsWithRestrictDecoration) {
+  const std::string kTest = R"(
+               OpCapability PhysicalStorageBufferAddresses
+; CHECK:       OpCapability PhysicalStorageBufferAddresses
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 2 4
+               OpDecorate %var RestrictPointer
+       %void = OpTypeVoid
+        %int = OpTypeInt 32 0
+     %struct = OpTypeStruct %int
+        %ptr = OpTypePointer Function %struct
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %6 = OpLabel
+        %var = OpVariable %ptr Function
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       PhysicalStorageBuffer_RemainsWithAliasedDecoration) {
+  const std::string kTest = R"(
+               OpCapability PhysicalStorageBufferAddresses
+; CHECK:       OpCapability PhysicalStorageBufferAddresses
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 2 4
+               OpDecorate %var AliasedPointer
+       %void = OpTypeVoid
+        %int = OpTypeInt 32 0
+     %struct = OpTypeStruct %int
+        %ptr = OpTypePointer Function %struct
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %6 = OpLabel
+        %var = OpVariable %ptr Function
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools