[spirv-reader][ir] Add image read support.

Add support for `OpImageRead`.

Bug: 407374442
Change-Id: Ib2674b39d3441e901a66e2d8cde7f6d33058c738
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/243395
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/spirv/reader/lower/builtins.cc b/src/tint/lang/spirv/reader/lower/builtins.cc
index d47142b..48c7c18 100644
--- a/src/tint/lang/spirv/reader/lower/builtins.cc
+++ b/src/tint/lang/spirv/reader/lower/builtins.cc
@@ -236,6 +236,7 @@
                     // Ignore Atomics, they'll be handled by the `Atomics` transform.
                     break;
                 case spirv::BuiltinFn::kSampledImage:
+                case spirv::BuiltinFn::kImageRead:
                 case spirv::BuiltinFn::kImageFetch:
                 case spirv::BuiltinFn::kImageGather:
                 case spirv::BuiltinFn::kImageQueryLevels:
diff --git a/src/tint/lang/spirv/reader/lower/texture.cc b/src/tint/lang/spirv/reader/lower/texture.cc
index 77d3ad1..c42a23d 100644
--- a/src/tint/lang/spirv/reader/lower/texture.cc
+++ b/src/tint/lang/spirv/reader/lower/texture.cc
@@ -104,6 +104,7 @@
             if (auto* builtin = inst->As<spirv::ir::BuiltinCall>()) {
                 switch (builtin->Func()) {
                     case spirv::BuiltinFn::kSampledImage:
+                    case spirv::BuiltinFn::kImageRead:
                     case spirv::BuiltinFn::kImageFetch:
                     case spirv::BuiltinFn::kImageGather:
                     case spirv::BuiltinFn::kImageQueryLevels:
@@ -128,6 +129,9 @@
                 case spirv::BuiltinFn::kSampledImage:
                     SampledImage(builtin);
                     break;
+                case spirv::BuiltinFn::kImageRead:
+                    ImageFetch(builtin);
+                    break;
                 case spirv::BuiltinFn::kImageFetch:
                     ImageFetch(builtin);
                     break;
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index 5a8fac1..21605a1 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -1394,7 +1394,10 @@
                     EmitSampledImage(inst);
                     break;
                 case spv::Op::OpImageFetch:
-                    EmitImageFetch(inst);
+                    EmitImageFetchOrRead(inst, spirv::BuiltinFn::kImageFetch);
+                    break;
+                case spv::Op::OpImageRead:
+                    EmitImageFetchOrRead(inst, spirv::BuiltinFn::kImageRead);
                     break;
                 case spv::Op::OpImageGather:
                     EmitImageGather(inst);
@@ -1459,7 +1462,7 @@
              inst.result_id());
     }
 
-    void EmitImageFetch(const spvtools::opt::Instruction& inst) {
+    void EmitImageFetchOrRead(const spvtools::opt::Instruction& inst, spirv::BuiltinFn fn) {
         auto sampled_image = Value(inst.GetSingleWordInOperand(0));
         auto* coord = Value(inst.GetSingleWordInOperand(1));
 
@@ -1476,9 +1479,7 @@
             args.Push(b_.Zero(ty_.u32()));
         }
 
-        Emit(b_.Call<spirv::ir::BuiltinCall>(Type(inst.type_id()), spirv::BuiltinFn::kImageFetch,
-                                             args),
-             inst.result_id());
+        Emit(b_.Call<spirv::ir::BuiltinCall>(Type(inst.type_id()), fn, args), inst.result_id());
     }
 
     void EmitImageGather(const spvtools::opt::Instruction& inst) {
diff --git a/src/tint/lang/spirv/reader/texture_test.cc b/src/tint/lang/spirv/reader/texture_test.cc
index b5d0e3f..fef7162 100644
--- a/src/tint/lang/spirv/reader/texture_test.cc
+++ b/src/tint/lang/spirv/reader/texture_test.cc
@@ -2428,8 +2428,8 @@
     %4:u32 = textureNumLevels %3)",
                          }));
 
-using MultiSampledImageAccessTest = SpirvReaderTestWithParam<ImgData>;
-TEST_P(MultiSampledImageAccessTest, Variable) {
+using NoSamplerImageAccessTest = SpirvReaderTestWithParam<ImgData>;
+TEST_P(NoSamplerImageAccessTest, Variable) {
     auto& params = GetParam();
     EXPECT_IR(R"(
             OpCapability Shader
@@ -2448,6 +2448,7 @@
     %uint = OpTypeInt 32  0
    %v2int = OpTypeVector %int 2
    %v3int = OpTypeVector %int 3
+   %v4int = OpTypeVector %int 4
   %v2uint = OpTypeVector %uint 2
   %v3uint = OpTypeVector %uint 3
   %v4uint = OpTypeVector %uint 4
@@ -2494,35 +2495,38 @@
 )");
 }
 
-INSTANTIATE_TEST_SUITE_P(DISABLED_SpirvReaderTest_ConvertResultSignedness,
-                         MultiSampledImageAccessTest,
+INSTANTIATE_TEST_SUITE_P(SpirvReaderTest_CheckResultSignedness,
+                         NoSamplerImageAccessTest,
                          ::testing::Values(
                              ImgData{
                                  .name = "no conversion, float -> v4float",
                                  .spirv_type = "%float 2D 0 0 0 2 Rgba32f",
                                  .spirv_fn = "%99 = OpImageRead %v4float %im %vi12",
-                                 .wgsl_type = "texture_2d<f32>",
-                                 .wgsl_fn = "let x_99 = textureLoad(x_20, vi12, 0i)",
+                                 .wgsl_type = "texture_storage_2d<rgba32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec4<f32> = textureLoad %3, vec2<i32>(1i, 2i))",
                              },
                              ImgData{
                                  .name = "no conversion, uint -> v4uint",
                                  .spirv_type = "%uint 2D 0 0 0 2 Rgba32ui",
                                  .spirv_fn = "%99 = OpImageRead %v4uint %im %vi12",
-                                 .wgsl_type = "texture_2d<u32>",
-                                 .wgsl_fn = "let x_99 = textureLoad(x_20, vi12, 0i)",
+                                 .wgsl_type = "texture_storage_2d<rgba32uint, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec4<u32> = textureLoad %3, vec2<i32>(1i, 2i))",
                              },
                              ImgData{
                                  .name = "no conversion, int -> v4int",
                                  .spirv_type = "%int 2D 0 0 0 2 Rgba32i",
                                  .spirv_fn = "%99 = OpImageRead %v4int %im %vi12",
-                                 .wgsl_type = "texture_2d<i32>",
-                                 .wgsl_fn = "let x_99 = textureLoad(x_20, vi12, 0i)",
+                                 .wgsl_type = "texture_storage_2d<rgba32sint, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec4<i32> = textureLoad %3, vec2<i32>(1i, 2i))",
                              }));
 
 // ImageQuerySize requires storage image or multisampled
 // For storage image, use another instruction to indicate whether it is readonly or writeonly.
 INSTANTIATE_TEST_SUITE_P(SpirvReaderTest_ImageQuerySize_NonArrayed_SignedResult,
-                         MultiSampledImageAccessTest,
+                         NoSamplerImageAccessTest,
                          ::testing::Values(
                              ImgData{
                                  .name = "1D storage image",
@@ -2564,7 +2568,7 @@
 // ImageQuerySize requires storage image or multisampled
 // For storage image, use another instruction to indicate whether it is readonly or writeonly.
 INSTANTIATE_TEST_SUITE_P(SpirvReaderTest_ImageQuerySize_Arrayed_SignedResult,
-                         MultiSampledImageAccessTest,
+                         NoSamplerImageAccessTest,
                          ::testing::Values(ImgData{
                              .name = "2D array storage image",
                              .spirv_type = "%float 2D 0 1 0 2 Rgba32f",
@@ -2580,7 +2584,7 @@
 // ImageQuerySize requires storage image or multisampled
 // For storage image, use another instruction to indicate whether it is readonly or writeonly.
 INSTANTIATE_TEST_SUITE_P(SpirvReaderTest_ImageQuerySize_NonArrayed_UnsignedResult,
-                         MultiSampledImageAccessTest,
+                         NoSamplerImageAccessTest,
                          ::testing::Values(
                              ImgData{
                                  .name = "1D storage image",
@@ -2616,7 +2620,7 @@
                              }));
 
 INSTANTIATE_TEST_SUITE_P(SpirvReaderTest_ImageQuerySize_Arrayed_UnsignedResult,
-                         MultiSampledImageAccessTest,
+                         NoSamplerImageAccessTest,
                          ::testing::Values(ImgData{
                              .name = "2D array storage image",
                              .spirv_type = "%float 2D 0 1 0 2 Rgba32f",
@@ -2629,7 +2633,7 @@
                          }));
 
 INSTANTIATE_TEST_SUITE_P(SpirvReaderTest_ImageQuerySamples_SignedResult,
-                         MultiSampledImageAccessTest,
+                         NoSamplerImageAccessTest,
                          ::testing::Values(ImgData{
                              .name = "Multsample 2D",
                              .spirv_type = "%float 2D 0 0 1 1 Unknown",
@@ -2642,7 +2646,7 @@
 
 // Translation must inject a type coercion from unsigned to signed.
 INSTANTIATE_TEST_SUITE_P(SpirvReaderTest_ImageQuerySamples_UnsignedResult,
-                         MultiSampledImageAccessTest,
+                         NoSamplerImageAccessTest,
                          ::testing::Values(ImgData{
                              .name = "Multisample 2D",
                              .spirv_type = "%float 2D 0 0 1 1 Unknown",
@@ -3096,13 +3100,14 @@
     auto& params = GetParam();
     EXPECT_IR(R"(
            OpCapability Shader
+           OpCapability Image1D
+           OpCapability Sampled1D
            OpMemoryModel Logical Simple
            OpEntryPoint Fragment %100 "main"
            OpExecutionMode %100 OriginUpperLeft
-           OpDecorate %10 DescriptorSet 0
-           OpDecorate %10 Binding 0
+           OpName %20 "wg"
            OpDecorate %20 DescriptorSet 2
-           OpDecorate %20 Binding 1
+           OpDecorate %20 Binding 0
 
    %void = OpTypeVoid
  %voidfn = OpTypeFunction %void
@@ -3133,19 +3138,15 @@
   %vu123 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3
  %vf1234 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
 
-%sampler = OpTypeSampler
-%ptr_sampler = OpTypePointer UniformConstant %sampler
   %im_ty = OpTypeImage )" +
                   params.spirv_type + R"(
 %ptr_im_ty = OpTypePointer UniformConstant %im_ty
 
-     %10 = OpVariable %ptr_sampler UniformConstant
      %20 = OpVariable %ptr_im_ty UniformConstant
 
      %100 = OpFunction %void None %voidfn
    %entry = OpLabel
 
-    %sam = OpLoad %sampler %10
      %im = OpLoad %im_ty %20
 )" + params.spirv_fn +
                   R"(
@@ -3154,132 +3155,129 @@
 )",
               R"(
 $B1: {  # root
-  %1:ptr<handle, sampler, read> = var undef @binding_point(0, 0)
   %wg:ptr<handle, )" +
                   params.wgsl_type +
-                  R"(<f32>, read> = var undef @binding_point(2, 0)
+                  R"(, read> = var undef @binding_point(2, 0)
 }
 
 %main = @fragment func():void {
   $B2: {
-    %4:)" + params.wgsl_type +
-                  R"(<f32> = load %wg
-    %5:sampler = load %1
-    %6:vec4<f32> = )" +
-                  params.wgsl_fn +
-                  R"(
+    %3:)" + params.wgsl_type +
+                  R"( = load %wg)" + params.wgsl_fn + R"(
     ret
   }
 }
 )");
 }
 
-// In SPIR-V, image read, fetch, and write use integer coordinates. Prove that we preserve signed
-// integer coordinates.
-INSTANTIATE_TEST_SUITE_P(DISABLED_SpirvReaderTest_PreserveIntCoords_NonArrayed,
+INSTANTIATE_TEST_SUITE_P(SpirvReaderTest,
                          NonSampledImageCoordsTest,
                          ::testing::Values(
                              ImgData{
-                                 .name = "1D Read",
+                                 .name = "1D Read signed",
                                  .spirv_type = "%float 1D 0 0 0 2 R32f",
                                  .spirv_fn = "%result = OpImageRead %v4float %im %int_1",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "i1",
+                                 .wgsl_type = "texture_storage_1d<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec4<f32> = textureLoad %3, 1i)",
                              },
                              ImgData{
-                                 .name = "1D Write",
+                                 .name = "1D Write signed",
                                  .spirv_type = "%float 1D 0 0 0 2 R32f",
                                  .spirv_fn = "OpImageWrite %im %int_1 %vf1234",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "i1",
+                                 .wgsl_type = "texture_storage_1d<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:void = textureStore %3, 1i, vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f))",
                              },
                              ImgData{
-                                 .name = "2D read",
+                                 .name = "2D read signed",
                                  .spirv_type = "%float 2D 0 0 0 2 R32f",
                                  .spirv_fn = "%result = OpImageRead %v4float %im %vi12",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "vi12",
+                                 .wgsl_type = "texture_storage_2d<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec4<f32> = textureLoad %3, vec2<i32>(1i, 2i))",
                              },
                              ImgData{
-                                 .name = "2D write",
+                                 .name = "2D write signed",
                                  .spirv_type = "%float 2D 0 0 0 2 R32f",
                                  .spirv_fn = "OpImageWrite %im %vi12 %vf1234",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "vi12",
-                             }));
-
-// In SPIR-V, image read, fetch, and write use integer coordinates. Prove that we preserve signed
-// integer coordinates.
-INSTANTIATE_TEST_SUITE_P(DISABLED_SpirvReaderTest_PreserveIntCoords_Arrayed,
-                         NonSampledImageCoordsTest,
-                         ::testing::Values(
+                                 .wgsl_type = "texture_storage_2d<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:void = textureStore %3, vec2<i32>(1i, 2i), vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f))",
+                             },
                              ImgData{
-                                 .name = "2D read",
+                                 .name = "2D array read signed",
                                  .spirv_type = "%float 2D 0 1 0 2 R32f",
                                  .spirv_fn = "%result = OpImageRead %v4float %im %vi123",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "vi123.xy",
+                                 .wgsl_type = "texture_storage_2d_array<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec2<i32> = swizzle vec3<i32>(1i, 2i, 3i), xy
+    %5:i32 = swizzle vec3<i32>(1i, 2i, 3i), z
+    %6:vec4<f32> = textureLoad %3, %4, %5)",
                              },
                              ImgData{
-                                 .name = "2D write",
+                                 .name = "2D array write signed",
                                  .spirv_type = "%float 2D 0 1 0 2 R32f",
                                  .spirv_fn = "OpImageWrite %im %vi123 %vf1234",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "vi123.xy",
-                             }));
-
-// In SPIR-V, image read, fetch, and write use integer coordinates. Prove that we convert unsigned
-// integer coordinates to signed.
-INSTANTIATE_TEST_SUITE_P(DISABLED_SpirvReaderTest_ConvertUintCoords_NonArrayed,
-                         NonSampledImageCoordsTest,
-                         ::testing::Values(
+                                 .wgsl_type = "texture_storage_2d_array<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec2<i32> = swizzle vec3<i32>(1i, 2i, 3i), xy
+    %5:i32 = swizzle vec3<i32>(1i, 2i, 3i), z
+    %6:void = textureStore %3, %4, %5, vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f))",
+                             },
                              ImgData{
-                                 .name = "1D read",
+                                 .name = "1D read unsigned",
                                  .spirv_type = "%float 1D 0 0 0 2 R32f",
                                  .spirv_fn = "%result = OpImageRead %v4float %im %uint_1",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "i32(u1)",
+                                 .wgsl_type = "texture_storage_1d<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec4<f32> = textureLoad %3, 1u)",
                              },
                              ImgData{
-                                 .name = "1D write",
+                                 .name = "1D write unsigned",
                                  .spirv_type = "%float 1D 0 0 0 2 R32f",
                                  .spirv_fn = "OpImageWrite %im %uint_1 %vf1234",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "i32(u1)",
+                                 .wgsl_type = "texture_storage_1d<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:void = textureStore %3, 1u, vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f))",
                              },
                              ImgData{
-                                 .name = "2D read",
+                                 .name = "2D read unsigned",
                                  .spirv_type = "%float 2D 0 0 0 2 R32f",
                                  .spirv_fn = "%result = OpImageRead %v4float %im %vu12",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "vec2i(vu12)",
+                                 .wgsl_type = "texture_storage_2d<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec4<f32> = textureLoad %3, vec2<u32>(1u, 2u))",
                              },
                              ImgData{
-                                 .name = "2D write",
+                                 .name = "2D write unsigned",
                                  .spirv_type = "%float 2D 0 0 0 2 R32f",
                                  .spirv_fn = "OpImageWrite %im %vu12 %vf1234",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "vec2i(vu12)",
-                             }));
-
-// In SPIR-V, image read, fetch, and write use integer coordinates. Prove that we convert unsigned
-// integer coordinates to signed.
-INSTANTIATE_TEST_SUITE_P(DISABLED_SpirvReaderTest_ConvertUintCoords_Arrayed,
-                         NonSampledImageCoordsTest,
-                         ::testing::Values(
-                             ImgData{
-                                 .name = "2D read",
-                                 .spirv_type = "%float 2D 0 1 0 2 R32f",
-                                 .spirv_fn = "%result = OpImageRead %v4float %im %vu123",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "vec2i(vu123.xy)",
+                                 .wgsl_type = "texture_storage_2d<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:void = textureStore %3, vec2<u32>(1u, 2u), vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f))",
                              },
                              ImgData{
-                                 .name = "2D write",
+                                 .name = "2D array read unsigned",
+                                 .spirv_type = "%float 2D 0 1 0 2 R32f",
+                                 .spirv_fn = "%result = OpImageRead %v4float %im %vu123",
+                                 .wgsl_type = "texture_storage_2d_array<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec2<u32> = swizzle vec3<u32>(1u, 2u, 3u), xy
+    %5:u32 = swizzle vec3<u32>(1u, 2u, 3u), z
+    %6:i32 = convert %5
+    %7:vec4<f32> = textureLoad %3, %4, %6)",
+                             },
+                             ImgData{
+                                 .name = "2D array write unsigned",
                                  .spirv_type = "%float 2D 0 1 0 2 R32f",
                                  .spirv_fn = "OpImageWrite %im %vu123 %vf1234",
-                                 .wgsl_type = "",
-                                 .wgsl_fn = "vec2i(vu123.xy)",
+                                 .wgsl_type = "texture_storage_2d_array<r32float, read_write>",
+                                 .wgsl_fn = R"(
+    %4:vec2<u32> = swizzle vec3<u32>(1u, 2u, 3u), xy
+    %5:u32 = swizzle vec3<u32>(1u, 2u, 3u), z
+    %6:i32 = convert %5
+    %7:void = textureStore %3, %4, %6, vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f))",
                              }));
 
 // An ad-hoc test to prove we never had the issue feared in crbug.com/tint/265.
@@ -3742,7 +3740,7 @@
 )");
 }
 
-TEST_F(SpirvReaderTest, DISABLED_ReadWriteStorageTexture) {
+TEST_F(SpirvReaderTest, ReadWriteStorageTexture) {
     EXPECT_IR(R"(
                OpCapability Shader
                OpCapability StorageImageExtendedFormats
@@ -3777,19 +3775,18 @@
                OpFunctionEnd
   )",
               R"(
-@group(0) @binding(0) var RWTexture2D : texture_storage_2d<rgba32float, read_write>;
-
-const x_9 = vec2u(1u);
-
-fn main_1() {
-  let x_31 = textureLoad(RWTexture2D, vec2i(x_9));
-  textureStore(RWTexture2D, vec2i(x_9), (x_31 + x_31));
-  return;
+$B1: {  # root
+  %RWTexture2D:ptr<handle, texture_storage_2d<rgba32float, read_write>, read> = var undef @binding_point(0, 0)
 }
 
-@compute @workgroup_size(8i, 8i, 1i)
-fn main() {
-  main_1();
+%main = @compute @workgroup_size(8u, 8u, 1u) func():void {
+  $B2: {
+    %3:texture_storage_2d<rgba32float, read_write> = load %RWTexture2D
+    %4:vec4<f32> = textureLoad %3, vec2<u32>(1u)
+    %5:vec4<f32> = add %4, %4
+    %6:void = textureStore %3, vec2<u32>(1u), %5
+    ret
+  }
 }
 )");
 }