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

Issue:
spirv-opt: Remove unneeded OpCapability
This is a very simple fragment shader in HLSL:
```hlsl
struct PSInput
{
	float2 color : COLOR;
};

struct Foo
{
    uint64_t u;
};

ConstantBuffer<Foo> u_foo; // Not used anywhere

float4 PSMain(PSInput input) : SV_TARGET
{
    return float4(0, 0, 0, 0);
}
```

spirv-opt does eliminate the `u_foo` just fine but it leaves the `OpCapability Int64`. If I comment out the declaration of `u_foo` myself I'll get the same SPIR-V as before without `OpCapability Int64`. 

It might be beneficial to add some functionality that removes unneeded `OpCapability`. Unless it's there and I missed it.

PS: If you want to play with the shader and see the SPIR-V use this https://shader-playground.timjones.io/c4707a75f904a7eae04110fddb78fa92 


Discussion / hints:
No we have not implemented anything like this.  Having a pass that does not, but is not part of the predefined transformation sequences would be fine, if you want to contribute it.  I don't know when I will get around to doing it myself.

There was debate about what having a capability in the spir-v mean.  Should it be an indication of the things that can be done, freeing tools to add code to use anything those capabilities define or is it the list of capabilities used by the shader?  We opted to be conservative, and not remove or add capabilities unless the pass is specifically intended to do that.  Those passes do not become part of the size or optimization sets.
Hello!
FYI there is a new pass in SPIR-V Tools to trim those unused capabilities.
It is also integrated in DXC. What's missing is the support for the Int64 capability, that can be added (https://github.com/KhronosGroup/SPIRV-Tools/blob/main/source/opt/trim_capabilities_pass.h#L75).

I will have to focus on raytracing capabilities first, and then this one. If you want to move faster, you can send a PR to SPIRV-Tools and request me for review.
Thank you for looking into this. There is no rush from my side. I may try to add this myself when I find some time but I'll notify you first to avoid duplicate work. 

Fix patch:
diff --git a/source/opt/trim_capabilities_pass.cpp b/source/opt/trim_capabilities_pass.cpp
index f8e4d81871..6c6b4a0998 100644
--- a/source/opt/trim_capabilities_pass.cpp
+++ b/source/opt/trim_capabilities_pass.cpp
@@ -40,6 +40,7 @@ constexpr uint32_t kOpTypePointerStorageClassIndex = 0;
 constexpr uint32_t kTypeArrayTypeIndex = 0;
 constexpr uint32_t kOpTypeScalarBitWidthIndex = 0;
 constexpr uint32_t kTypePointerTypeIdInIdx = 1;
+constexpr uint32_t kOpTypeIntSizeIndex = 0;
 
 // DFS visit of the type defined by `instruction`.
 // If `condition` is true, children of the current node are visited.
@@ -255,13 +256,24 @@ static std::optional<spv::Capability> Handler_OpTypePointer_StorageUniform16(
                         : std::nullopt;
 }
 
+static std::optional<spv::Capability> Handler_OpTypeInt_Int64(
+    const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypeInt &&
+         "This handler only support OpTypeInt opcodes.");
+
+  const uint32_t size =
+      instruction->GetSingleWordInOperand(kOpTypeIntSizeIndex);
+  return size == 64 ? std::optional(spv::Capability::Int64) : std::nullopt;
+}
+
 // Opcode of interest to determine capabilities requirements.
-constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 4> kOpcodeHandlers{{
+constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 5> kOpcodeHandlers{{
     // clang-format off
     {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageInputOutput16},
     {spv::Op::OpTypePointer, Handler_OpTypePointer_StoragePushConstant16},
     {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniformBufferBlock16},
-    {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniform16}
+    {spv::Op::OpTypePointer, Handler_OpTypePointer_StorageUniform16},
+    {spv::Op::OpTypeInt, Handler_OpTypeInt_Int64 },
     // clang-format on
 }};
 
diff --git a/source/opt/trim_capabilities_pass.h b/source/opt/trim_capabilities_pass.h
index 16b5781aea..2728617990 100644
--- a/source/opt/trim_capabilities_pass.h
+++ b/source/opt/trim_capabilities_pass.h
@@ -78,6 +78,7 @@ class TrimCapabilitiesPass : public Pass {
       spv::Capability::FragmentShaderSampleInterlockEXT,
       spv::Capability::FragmentShaderShadingRateInterlockEXT,
       spv::Capability::Groups,
+      spv::Capability::Int64,
       spv::Capability::Linkage,
       spv::Capability::MinLod,
       spv::Capability::Shader,


Test patch:
diff --git a/test/opt/trim_capabilities_pass_test.cpp b/test/opt/trim_capabilities_pass_test.cpp
index a791fe2883..c604e2b2eb 100644
--- a/test/opt/trim_capabilities_pass_test.cpp
+++ b/test/opt/trim_capabilities_pass_test.cpp
@@ -1588,6 +1588,45 @@ TEST_F(TrimCapabilitiesPassTest,
   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
 }
 
+TEST_F(TrimCapabilitiesPassTest, Int64_RemovedWhenUnused) {
+  const std::string kTest = R"(
+               OpCapability Int64
+; CHECK-NOT:   OpCapability Int64
+               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, Int64_RemainsWhenUsed) {
+  const std::string kTest = R"(
+               OpCapability Int64
+; CHECK:       OpCapability Int64
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+        %int = OpTypeInt 64 0
+          %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::SuccessWithoutChange);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools