[spirv-reader][ir] Convert sample index when needed.

In SPIR-V the sample index can be i32 or u32. In WGSL it must be u32.
Make sure we do any required conversions to match types.

Bug: 42250952
Change-Id: I601ff5312278f871ab34622a4b26d5f6b0ea8634
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/245714
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/spirv/reader/lower/shader_io.cc b/src/tint/lang/spirv/reader/lower/shader_io.cc
index 5e41801..1676dc6 100644
--- a/src/tint/lang/spirv/reader/lower/shader_io.cc
+++ b/src/tint/lang/spirv/reader/lower/shader_io.cc
@@ -416,7 +416,8 @@
                         break;
                     }
                     case core::BuiltinValue::kInstanceIndex:
-                    case core::BuiltinValue::kLocalInvocationIndex: {
+                    case core::BuiltinValue::kLocalInvocationIndex:
+                    case core::BuiltinValue::kSampleIndex: {
                         var_type = ty.u32();
                         break;
                     }
@@ -483,7 +484,8 @@
                         break;
                     }
                     case core::BuiltinValue::kInstanceIndex:
-                    case core::BuiltinValue::kLocalInvocationIndex: {
+                    case core::BuiltinValue::kLocalInvocationIndex:
+                    case core::BuiltinValue::kSampleIndex: {
                         auto* idx_ty = var->Result()->Type()->UnwrapPtr();
                         if (idx_ty->IsSignedIntegerScalar()) {
                             auto* conv = b.Convert(ty.i32(), result);
diff --git a/src/tint/lang/spirv/reader/lower/shader_io_test.cc b/src/tint/lang/spirv/reader/lower/shader_io_test.cc
index 731faee..94e4942 100644
--- a/src/tint/lang/spirv/reader/lower/shader_io_test.cc
+++ b/src/tint/lang/spirv/reader/lower/shader_io_test.cc
@@ -2531,6 +2531,94 @@
     EXPECT_EQ(expect, str());
 }
 
+TEST_F(SpirvReader_ShaderIOTest, SampleIndex_i32) {
+    auto* idx = b.Var("idx", ty.ptr(core::AddressSpace::kIn, ty.i32()));
+    idx->SetBuiltin(core::BuiltinValue::kSampleIndex);
+    mod.root_block->Append(idx);
+
+    auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+    b.Append(ep->Block(), [&] {
+        auto* idx_value = b.Load(idx);
+        b.Let("a", b.Multiply(ty.i32(), idx_value, 2_i));
+        b.Return(ep);
+    });
+
+    auto* src = R"(
+$B1: {  # root
+  %idx:ptr<__in, i32, read> = var undef @builtin(sample_index)
+}
+
+%foo = @fragment func():void {
+  $B2: {
+    %3:i32 = load %idx
+    %4:i32 = mul %3, 2i
+    %a:i32 = let %4
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = @fragment func(%idx:u32 [@sample_index]):void {
+  $B1: {
+    %3:i32 = convert %idx
+    %4:i32 = mul %3, 2i
+    %a:i32 = let %4
+    ret
+  }
+}
+)";
+
+    Run(ShaderIO);
+
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvReader_ShaderIOTest, SampleIndex_u32) {
+    auto* idx = b.Var("idx", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+    idx->SetBuiltin(core::BuiltinValue::kSampleIndex);
+    mod.root_block->Append(idx);
+
+    auto* ep = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+    b.Append(ep->Block(), [&] {
+        auto* idx_value = b.Load(idx);
+        b.Let("a", b.Multiply(ty.u32(), idx_value, 2_u));
+
+        b.Return(ep);
+    });
+
+    auto* src = R"(
+$B1: {  # root
+  %idx:ptr<__in, u32, read> = var undef @builtin(sample_index)
+}
+
+%foo = @fragment func():void {
+  $B2: {
+    %3:u32 = load %idx
+    %4:u32 = mul %3, 2u
+    %a:u32 = let %4
+    ret
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = @fragment func(%idx:u32 [@sample_index]):void {
+  $B1: {
+    %3:u32 = mul %idx, 2u
+    %a:u32 = let %3
+    ret
+  }
+}
+)";
+
+    Run(ShaderIO);
+
+    EXPECT_EQ(expect, str());
+}
+
 // Test that a sample mask array is converted to a scalar u32 for the entry point.
 TEST_F(SpirvReader_ShaderIOTest, SampleMask) {
     auto* arr = ty.array<u32, 1>();