[hlsl] Emit `swizzle` instructions

This CL adds `swizzle` support to the HLSL IR backend.

Bug: 42251045
Change-Id: Id3986fb5ef81536f8eba3a9003e61a86929c23b5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/194025
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/access_test.cc b/src/tint/lang/hlsl/writer/access_test.cc
index 087b791..84764f4 100644
--- a/src/tint/lang/hlsl/writer/access_test.cc
+++ b/src/tint/lang/hlsl/writer/access_test.cc
@@ -183,5 +183,47 @@
 )");
 }
 
+TEST_F(HlslWriterTest, AccessSwizzle) {
+    auto* f = b.Function("a", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+    f->SetWorkgroupSize(1, 1, 1);
+
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var("v", b.Zero<vec3<f32>>());
+        b.Let("b", b.Swizzle(ty.f32(), v, {1u}));
+        b.Return(f);
+    });
+
+    ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
+    EXPECT_EQ(output_.hlsl, R"(
+[numthreads(1, 1, 1)]
+void a() {
+  float3 v = (0.0f).xxx;
+  float b = v.y;
+}
+
+)");
+}
+
+TEST_F(HlslWriterTest, AccessSwizzleMulti) {
+    auto* f = b.Function("a", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+    f->SetWorkgroupSize(1, 1, 1);
+
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var("v", b.Zero<vec4<f32>>());
+        b.Let("b", b.Swizzle(ty.vec4<f32>(), v, {3u, 2u, 1u, 0u}));
+        b.Return(f);
+    });
+
+    ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
+    EXPECT_EQ(output_.hlsl, R"(
+[numthreads(1, 1, 1)]
+void a() {
+  float4 v = (0.0f).xxxx;
+  float4 b = v.wzyx;
+}
+
+)");
+}
+
 }  // namespace
 }  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/printer/printer.cc b/src/tint/lang/hlsl/writer/printer/printer.cc
index 99dc490..e9d3229 100644
--- a/src/tint/lang/hlsl/writer/printer/printer.cc
+++ b/src/tint/lang/hlsl/writer/printer/printer.cc
@@ -387,12 +387,36 @@
                     [&](const core::ir::Let* l) { out << NameOf(l->Result(0)); },              //
                     [&](const core::ir::Load* l) { EmitLoad(out, l); },                        //
                     [&](const core::ir::UserCall* c) { EmitUserCall(out, c); },                //
+                    [&](const core::ir::Swizzle* s) { EmitSwizzle(out, s); },                  //
                     [&](const core::ir::Var* var) { out << NameOf(var->Result(0)); },          //
                     TINT_ICE_ON_NO_MATCH);
             },
             TINT_ICE_ON_NO_MATCH);
     }
 
+    void EmitSwizzle(StringStream& out, const core::ir::Swizzle* swizzle) {
+        EmitValue(out, swizzle->Object());
+        out << ".";
+        for (const auto i : swizzle->Indices()) {
+            switch (i) {
+                case 0:
+                    out << "x";
+                    break;
+                case 1:
+                    out << "y";
+                    break;
+                case 2:
+                    out << "z";
+                    break;
+                case 3:
+                    out << "w";
+                    break;
+                default:
+                    TINT_UNREACHABLE();
+            }
+        }
+    }
+
     /// Emit an access instruction
     void EmitAccess(StringStream& out, const core::ir::Access* a) {
         EmitValue(out, a->Object());