[ir][spirv-writer] Handle storage texture loads

Map these to OpImageRead instead of OpImageFetch, and swizzle the
result when loading from a bgra8unorm texture.

Bug: tint:1906, tint:2007
Change-Id: I6752d2532aad71025da74a18c06c3f5716fb275c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/149922
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.cc b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.cc
index 1c414d7..d4fcbf2 100644
--- a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.cc
@@ -149,7 +149,12 @@
                         swizzle->InsertBefore(call);
                         call->SetOperand(index, swizzle->Result());
                     } else if (call->Func() == core::Function::kTextureLoad) {
-                        TINT_ICE() << "unhandled bgra8unorm texture load";
+                        // Swizzle the result of a `textureLoad()` builtin.
+                        auto* swizzle =
+                            b.Swizzle(call->Result()->Type(), nullptr, Vector{2u, 1u, 0u, 3u});
+                        call->Result()->ReplaceAllUsesWith(swizzle->Result());
+                        swizzle->InsertAfter(call);
+                        swizzle->SetOperand(Swizzle::kObjectOperandOffset, call->Result());
                     }
                 },
                 [&](UserCall* call) {
diff --git a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill_test.cc b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill_test.cc
index a7974d9..e8d19a3 100644
--- a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill_test.cc
+++ b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill_test.cc
@@ -604,5 +604,117 @@
     EXPECT_EQ(expect, str());
 }
 
+TEST_F(IR_Bgra8UnormPolyfillTest, TextureLoad) {
+    auto format = core::TexelFormat::kBgra8Unorm;
+    auto* texture_ty =
+        ty.Get<core::type::StorageTexture>(core::type::TextureDimension::k2d, format, read,
+                                           core::type::StorageTexture::SubtypeFor(format, ty));
+
+    auto* var = b.Var("texture", ty.ptr(handle, texture_ty));
+    var->SetBindingPoint(1, 2);
+    b.RootBlock()->Append(var);
+
+    auto* func = b.Function("foo", ty.vec4<f32>());
+    auto* coords = b.FunctionParam("coords", ty.vec2<u32>());
+    func->SetParams({coords});
+    b.Append(func->Block(), [&] {
+        auto* load = b.Load(var->Result());
+        auto* result = b.Call(ty.vec4<f32>(), core::Function::kTextureLoad, load, coords);
+        b.Return(func, result);
+        mod.SetName(result, "result");
+    });
+
+    auto* src = R"(
+%b1 = block {  # root
+  %texture:ptr<handle, texture_storage_2d<bgra8unorm, read>, read_write> = var @binding_point(1, 2)
+}
+
+%foo = func(%coords:vec2<u32>):vec4<f32> -> %b2 {
+  %b2 = block {
+    %4:texture_storage_2d<bgra8unorm, read> = load %texture
+    %result:vec4<f32> = textureLoad %4, %coords
+    ret %result
+  }
+}
+)";
+    auto* expect = R"(
+%b1 = block {  # root
+  %texture:ptr<handle, texture_storage_2d<rgba8unorm, read>, read_write> = var @binding_point(1, 2)
+}
+
+%foo = func(%coords:vec2<u32>):vec4<f32> -> %b2 {
+  %b2 = block {
+    %4:texture_storage_2d<rgba8unorm, read> = load %texture
+    %result:vec4<f32> = textureLoad %4, %coords
+    %6:vec4<f32> = swizzle %result, zyxw
+    ret %6
+  }
+}
+)";
+
+    EXPECT_EQ(src, str());
+
+    Run(Bgra8UnormPolyfill);
+    EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_Bgra8UnormPolyfillTest, TextureLoadAndStore) {
+    auto format = core::TexelFormat::kBgra8Unorm;
+    auto* texture_ty =
+        ty.Get<core::type::StorageTexture>(core::type::TextureDimension::k2d, format, read_write,
+                                           core::type::StorageTexture::SubtypeFor(format, ty));
+
+    auto* var = b.Var("texture", ty.ptr(handle, texture_ty));
+    var->SetBindingPoint(1, 2);
+    b.RootBlock()->Append(var);
+
+    auto* func = b.Function("foo", ty.void_());
+    auto* coords = b.FunctionParam("coords", ty.vec2<u32>());
+    func->SetParams({coords});
+    b.Append(func->Block(), [&] {
+        auto* load = b.Load(var->Result());
+        auto* result = b.Call(ty.vec4<f32>(), core::Function::kTextureLoad, load, coords);
+        b.Call(ty.void_(), core::Function::kTextureStore, load, coords, result);
+        b.Return(func);
+        mod.SetName(result, "result");
+    });
+
+    auto* src = R"(
+%b1 = block {  # root
+  %texture:ptr<handle, texture_storage_2d<bgra8unorm, read_write>, read_write> = var @binding_point(1, 2)
+}
+
+%foo = func(%coords:vec2<u32>):void -> %b2 {
+  %b2 = block {
+    %4:texture_storage_2d<bgra8unorm, read_write> = load %texture
+    %result:vec4<f32> = textureLoad %4, %coords
+    %6:void = textureStore %4, %coords, %result
+    ret
+  }
+}
+)";
+    auto* expect = R"(
+%b1 = block {  # root
+  %texture:ptr<handle, texture_storage_2d<rgba8unorm, read_write>, read_write> = var @binding_point(1, 2)
+}
+
+%foo = func(%coords:vec2<u32>):void -> %b2 {
+  %b2 = block {
+    %4:texture_storage_2d<rgba8unorm, read_write> = load %texture
+    %result:vec4<f32> = textureLoad %4, %coords
+    %6:vec4<f32> = swizzle %result, zyxw
+    %7:vec4<f32> = swizzle %6, zyxw
+    %8:void = textureStore %4, %coords, %7
+    ret
+  }
+}
+)";
+
+    EXPECT_EQ(src, str());
+
+    Run(Bgra8UnormPolyfill);
+    EXPECT_EQ(expect, str());
+}
+
 }  // namespace
 }  // namespace tint::core::ir::transform
diff --git a/src/tint/lang/spirv/ir/intrinsic.cc b/src/tint/lang/spirv/ir/intrinsic.cc
index 2b9c5f6..8933c06 100644
--- a/src/tint/lang/spirv/ir/intrinsic.cc
+++ b/src/tint/lang/spirv/ir/intrinsic.cc
@@ -89,6 +89,9 @@
     if (str == "image_query_size_lod") {
         return Intrinsic::kImageQuerySizeLod;
     }
+    if (str == "image_read") {
+        return Intrinsic::kImageRead;
+    }
     if (str == "image_sample_dref_explicit_lod") {
         return Intrinsic::kImageSampleDrefExplicitLod;
     }
@@ -157,6 +160,8 @@
             return "image_query_size";
         case Intrinsic::kImageQuerySizeLod:
             return "image_query_size_lod";
+        case Intrinsic::kImageRead:
+            return "image_read";
         case Intrinsic::kImageSampleDrefExplicitLod:
             return "image_sample_dref_explicit_lod";
         case Intrinsic::kImageSampleDrefImplicitLod:
diff --git a/src/tint/lang/spirv/ir/intrinsic.h b/src/tint/lang/spirv/ir/intrinsic.h
index c8288c3..2b5f906 100644
--- a/src/tint/lang/spirv/ir/intrinsic.h
+++ b/src/tint/lang/spirv/ir/intrinsic.h
@@ -54,6 +54,7 @@
     kImageGather,
     kImageQuerySize,
     kImageQuerySizeLod,
+    kImageRead,
     kImageSampleDrefExplicitLod,
     kImageSampleDrefImplicitLod,
     kImageSampleExplicitLod,
@@ -101,6 +102,7 @@
     "image_gather",
     "image_query_size",
     "image_query_size_lod",
+    "image_read",
     "image_sample_dref_explicit_lod",
     "image_sample_dref_implicit_lod",
     "image_sample_explicit_lod",
diff --git a/src/tint/lang/spirv/spirv.def b/src/tint/lang/spirv/spirv.def
index 1422511..11ee7ec 100644
--- a/src/tint/lang/spirv/spirv.def
+++ b/src/tint/lang/spirv/spirv.def
@@ -62,6 +62,7 @@
   image_dref_gather
   image_query_size
   image_query_size_lod
+  image_read
   image_sample_implicit_lod
   image_sample_explicit_lod
   image_sample_dref_implicit_lod
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index eaad696..e2c94fe 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -1633,6 +1633,9 @@
             module_.PushCapability(SpvCapabilityImageQuery);
             op = spv::Op::OpImageQuerySizeLod;
             break;
+        case spirv::ir::Intrinsic::kImageRead:
+            op = spv::Op::OpImageRead;
+            break;
         case spirv::ir::Intrinsic::kImageSampleImplicitLod:
             op = spv::Op::OpImageSampleImplicitLod;
             break;
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
index 7616234..20290d5 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
@@ -673,8 +673,11 @@
         if (expects_scalar_result) {
             result_ty = ty.vec4(result_ty);
         }
-        auto* texture_call = b.Call<spirv::ir::IntrinsicCall>(
-            result_ty, spirv::ir::Intrinsic::kImageFetch, std::move(intrinsic_args));
+        auto intrinsic = texture_ty->Is<core::type::StorageTexture>()
+                             ? spirv::ir::Intrinsic::kImageRead
+                             : spirv::ir::Intrinsic::kImageFetch;
+        auto* texture_call =
+            b.Call<spirv::ir::IntrinsicCall>(result_ty, intrinsic, std::move(intrinsic_args));
         texture_call->InsertBefore(builtin);
         auto* result = texture_call->Result();