[tint][ir][ToProgram] Implement swizzles

Change-Id: I9bccd2178febb0748207eef38a2639d02a378693
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/140989
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/ir/to_program.cc b/src/tint/ir/to_program.cc
index 72050ac..58c7449 100644
--- a/src/tint/ir/to_program.cc
+++ b/src/tint/ir/to_program.cc
@@ -47,6 +47,7 @@
 #include "src/tint/ir/store.h"
 #include "src/tint/ir/store_vector_element.h"
 #include "src/tint/ir/switch.h"
+#include "src/tint/ir/swizzle.h"
 #include "src/tint/ir/unary.h"
 #include "src/tint/ir/unreachable.h"
 #include "src/tint/ir/user_call.h"
@@ -304,7 +305,8 @@
             [&](ir::Store* i) { Store(i); },                            //
             [&](ir::StoreVectorElement* i) { StoreVectorElement(i); },  //
             [&](ir::Switch* i) { Switch(i); },                          //
-            [&](ir::Unary* u) { Unary(u); },                            //
+            [&](ir::Swizzle* i) { Swizzle(i); },                        //
+            [&](ir::Unary* i) { Unary(i); },                            //
             [&](ir::Unreachable*) {},                                   //
             [&](ir::Var* i) { Var(i); },                                //
             [&](Default) { UNHANDLED_CASE(inst); });
@@ -652,6 +654,21 @@
         Bind(a->Result(), expr);
     }
 
+    void Swizzle(ir::Swizzle* s) {
+        auto* vec = Expr(s->Object());
+        utils::Vector<char, 4> components;
+        for (uint32_t i : s->Indices()) {
+            if (TINT_UNLIKELY(i >= 4)) {
+                TINT_ICE(IR, b.Diagnostics()) << "invalid swizzle index: " << i;
+                return;
+            }
+            components.Push("xyzw"[i]);
+        }
+        auto* swizzle =
+            b.MemberAccessor(vec, std::string_view(components.begin(), components.Length()));
+        Bind(s->Result(), swizzle);
+    }
+
     void Binary(ir::Binary* e) {
         if (e->Kind() == ir::Binary::Kind::kEqual) {
             auto* rhs = e->RHS()->As<ir::Constant>();
diff --git a/src/tint/ir/to_program_roundtrip_test.cc b/src/tint/ir/to_program_roundtrip_test.cc
index fe8eb02..6282218 100644
--- a/src/tint/ir/to_program_roundtrip_test.cc
+++ b/src/tint/ir/to_program_roundtrip_test.cc
@@ -783,6 +783,73 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// Swizzle
+////////////////////////////////////////////////////////////////////////////////
+TEST_F(IRToProgramRoundtripTest, Access_Vec3_Value_xy) {
+    Test(R"(
+fn f(v : vec3<f32>) -> vec2<f32> {
+  return v.xy;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Access_Vec3_Value_yz) {
+    Test(R"(
+fn f(v : vec3<f32>) -> vec2<f32> {
+  return v.yz;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Access_Vec3_Value_yzx) {
+    Test(R"(
+fn f(v : vec3<f32>) -> vec3<f32> {
+  return v.yzx;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Access_Vec3_Value_yzxy) {
+    Test(R"(
+fn f(v : vec3<f32>) -> vec4<f32> {
+  return v.yzxy;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Access_Vec3_Pointer_xy) {
+    Test(R"(
+fn f(v : ptr<function, vec3<f32>>) -> vec2<f32> {
+  return (*(v)).xy;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Access_Vec3_Pointer_yz) {
+    Test(R"(
+fn f(v : ptr<function, vec3<f32>>) -> vec2<f32> {
+  return (*(v)).yz;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Access_Vec3_Pointer_yzx) {
+    Test(R"(
+fn f(v : ptr<function, vec3<f32>>) -> vec3<f32> {
+  return (*(v)).yzx;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Access_Vec3_Pointer_yzxy) {
+    Test(R"(
+fn f(v : ptr<function, vec3<f32>>) -> vec4<f32> {
+  return (*(v)).yzxy;
+}
+)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // Unary ops
 ////////////////////////////////////////////////////////////////////////////////
 TEST_F(IRToProgramRoundtripTest, UnaryOp_Negate) {