opt: support 64-bit OpAccessChain index in FixStorageClass (#5446)

The SPIR-V specification allows any scalar integer type as an index. DXC
usually emits indexes as 32-bit integer types, however, in some cases it
is possible to make it emit 64-bit indexes instead (as in
https://github.com/microsoft/DirectXShaderCompiler/issues/5638).
diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp
index 5597e82..564cd1b 100644
--- a/source/opt/fix_storage_class.cpp
+++ b/source/opt/fix_storage_class.cpp
@@ -318,7 +318,13 @@
         const analysis::Constant* index_const =
             context()->get_constant_mgr()->FindDeclaredConstant(
                 inst->GetSingleWordInOperand(i));
-        uint32_t index = index_const->GetU32();
+        // It is highly unlikely that any type would have more fields than could
+        // be indexed by a 32-bit integer, and GetSingleWordInOperand only takes
+        // a 32-bit value, so we would not be able to handle it anyway. But the
+        // specification does allow any scalar integer type, treated as signed,
+        // so we simply downcast the index to 32-bits.
+        uint32_t index =
+            static_cast<uint32_t>(index_const->GetSignExtendedValue());
         id = type_inst->GetSingleWordInOperand(index);
         break;
       }
diff --git a/test/opt/fix_storage_class_test.cpp b/test/opt/fix_storage_class_test.cpp
index 93ce873..684e006 100644
--- a/test/opt/fix_storage_class_test.cpp
+++ b/test/opt/fix_storage_class_test.cpp
@@ -874,6 +874,47 @@
 
   SinglePassRunAndMatch<FixStorageClass>(text, false);
 }
+TEST_F(FixStorageClassTest, SupportsU64Index) {
+  const std::string text = R"(
+; CHECK: OpAccessChain %_ptr_Uniform_float
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "testMain" %gl_LocalInvocationID
+               OpExecutionMode %1 LocalSize 8 8 1
+               OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
+               OpDecorate %8 DescriptorSet 0
+               OpDecorate %8 Binding 0
+               OpDecorate %_runtimearr_float ArrayStride 4
+               OpMemberDecorate %_struct_7 0 Offset 0
+               OpDecorate %_struct_7 BufferBlock
+      %ulong = OpTypeInt 64 0
+    %ulong_0 = OpConstant %ulong 0
+      %float = OpTypeFloat 32
+  %float_123 = OpConstant %float 123
+       %uint = OpTypeInt 32 0
+    %uint_10 = OpConstant %uint 10
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
+     %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+       %void = OpTypeVoid
+         %30 = OpTypeFunction %void
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+          %8 = OpVariable %_ptr_Uniform__struct_7 Uniform
+%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
+          %1 = OpFunction %void None %30
+         %38 = OpLabel
+         %44 = OpLoad %v3uint %gl_LocalInvocationID
+         %59 = OpCompositeExtract %uint %44 0
+         %60 = OpAccessChain %_ptr_Uniform_float %8 %ulong_0 %59
+               OpStore %60 %float_123
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FixStorageClass>(text, false);
+}
 
 }  // namespace
 }  // namespace opt