KhronosGroup / SPIRV-Tools

Apache License 2.0
1.09k stars 559 forks source link

SPIR-V optimiser unable to remove redundant specialisation constants #5858

Open McDaMastR opened 1 month ago

McDaMastR commented 1 month ago

The SPIR-V optimiser spirv-opt includes multiple options with the purpose of removing redundant values, including the following.

However, none of the above related optimisations are able to remove a redundant specialisation constant, where 'redundant' means it shares the same SpecId and default value with a separately declared <id>. The following assembly is of the module test.spvasm.

               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %ep1 "ep1" %buffer
               OpEntryPoint GLCompute %ep2 "ep2" %buffer
               OpExecutionMode %ep1 LocalSize 1 1 1
               OpExecutionMode %ep2 LocalSize 1 1 1

               OpDecorate %spec1 SpecId 0
               OpDecorate %spec2 SpecId 0
               OpDecorate %spec3 SpecId 1
               OpMemberDecorate %struct 0 Offset 0
               OpMemberDecorate %struct 1 Offset 4
               OpDecorate %struct Block

       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
     %struct = OpTypeStruct %uint %uint
       %func = OpTypeFunction %void

     %uint_0 = OpConstant %uint 0
     %uint_1 = OpConstant %uint 1
      %spec1 = OpSpecConstant %uint 1
      %spec2 = OpSpecConstant %uint 1
      %spec3 = OpSpecConstant %uint 1

   %ptr_uint = OpTypePointer StorageBuffer %uint
 %ptr_struct = OpTypePointer StorageBuffer %struct
     %buffer = OpVariable %ptr_struct StorageBuffer

        %ep1 = OpFunction %void None %func
     %label1 = OpLabel
       %val1 =   OpIAdd %uint %spec1 %spec3
       %ptr1 =   OpAccessChain %ptr_uint %buffer %uint_0
                 OpStore %ptr1 %val1
                 OpReturn
               OpFunctionEnd

        %ep2 = OpFunction %void None %func
     %label2 = OpLabel
       %val2 =   OpIAdd %uint %spec2 %spec3
       %ptr2 =   OpAccessChain %ptr_uint %buffer %uint_1
                 OpStore %ptr2 %val2
                 OpReturn
               OpFunctionEnd

The two entry points add two specialisation constants together, and store the result into a storage buffer. ep1 adds spec1 and spec3, and ep2 adds spec2 and spec3. All specialisation constants have the same default value, and spec1 and spec2 have the same SpecId, making spec2 redundant. spec3 is included as an example of what would happen if spec2 was removed due to its redundancy, and was hence replaced or combined with spec1.

If you assemble this module into SPIR-V, apply the optimisations listed above, and then disassemble back into assembly as follows:

spirv-as --target-env spv1.6 -o test.spv test.spvasm
spirv-opt --target-env=spv1.6 --eliminate-dead-const --remove-duplicates --unify-const -o test2.spv test.spv
spirv-dis --no-header --nested-indent -o test2.spvasm test2.spv

The resultant module test2.spvasm will be equivalent to the original module test.spvasm, including containing the redundant specialisation constant. Both test.spv and test2.spv pass the SPIR-V validator, and all SPIRV-Tools are of the following --version.

SPIRV-Tools v2024.4 v2024.4.rc1-0-g6dcc7e35