[ir] Add multi-element swizzle.

This CL adds support for multi-element swizzle.

Bug: tint:1913
Change-Id: I72dc831fcda041e764b0d1a509ed031d5e66849f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/135680
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 8200833..8434ede 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1271,6 +1271,8 @@
       "ir/store.h",
       "ir/switch.cc",
       "ir/switch.h",
+      "ir/swizzle.cc",
+      "ir/swizzle.h",
       "ir/transform/transform.cc",
       "ir/transform/transform.h",
       "ir/unary.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 7b13895..586b903 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -780,6 +780,8 @@
     ir/store.h
     ir/switch.cc
     ir/switch.h
+    ir/swizzle.cc
+    ir/swizzle.h
     ir/to_program.cc
     ir/to_program.h
     ir/unary.cc
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index bf42d5d..b93cb94 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -247,4 +247,10 @@
     return ir.values.Create<ir::Access>(type, source, indices);
 }
 
+ir::Swizzle* Builder::Swizzle(const type::Type* type,
+                              Value* source,
+                              utils::VectorRef<uint32_t> indices) {
+    return ir.values.Create<ir::Swizzle>(type, source, indices);
+}
+
 }  // namespace tint::ir
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index 6afac25..ae20e42 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -42,6 +42,7 @@
 #include "src/tint/ir/return.h"
 #include "src/tint/ir/store.h"
 #include "src/tint/ir/switch.h"
+#include "src/tint/ir/swizzle.h"
 #include "src/tint/ir/unary.h"
 #include "src/tint/ir/user_call.h"
 #include "src/tint/ir/value.h"
@@ -396,6 +397,13 @@
     /// @returns the instruction
     ir::Access* Access(const type::Type* type, Value* source, utils::VectorRef<Value*> indices);
 
+    /// Creates a new `Swizzle`
+    /// @param type the return type
+    /// @param source the source value
+    /// @param indices the access indices
+    /// @returns the instruction
+    ir::Swizzle* Swizzle(const type::Type* type, Value* source, utils::VectorRef<uint32_t> indices);
+
     /// Retrieves the root block for the module, creating if necessary
     /// @returns the root block
     ir::Block* CreateRootBlockIfNeeded();
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index 7763366..40fbb2e 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -38,6 +38,7 @@
 #include "src/tint/ir/return.h"
 #include "src/tint/ir/store.h"
 #include "src/tint/ir/switch.h"
+#include "src/tint/ir/swizzle.h"
 #include "src/tint/ir/user_call.h"
 #include "src/tint/ir/var.h"
 #include "src/tint/switch.h"
@@ -400,6 +401,29 @@
             }
             out_ << std::endl;
         },
+        [&](const ir::Swizzle* s) {
+            EmitValueWithType(s);
+            out_ << " = swizzle ";
+            EmitValue(s->Object());
+            out_ << ", ";
+            for (auto idx : s->Indices()) {
+                switch (idx) {
+                    case 0:
+                        out_ << "x";
+                        break;
+                    case 1:
+                        out_ << "y";
+                        break;
+                    case 2:
+                        out_ << "z";
+                        break;
+                    case 3:
+                        out_ << "w";
+                        break;
+                }
+            }
+            out_ << std::endl;
+        },
         [&](const ir::Branch* b) { EmitBranch(b); },
         [&](Default) { out_ << "Unknown instruction: " << inst->TypeInfo().name; });
 }
diff --git a/src/tint/ir/from_program.cc b/src/tint/ir/from_program.cc
index 54e78fc..33b888b 100644
--- a/src/tint/ir/from_program.cc
+++ b/src/tint/ir/from_program.cc
@@ -878,6 +878,8 @@
 
     struct AccessorInfo {
         Value* object = nullptr;
+        Instruction* result = nullptr;
+        const type::Type* result_type = nullptr;
         utils::Vector<Value*, 1> indices;
     };
 
@@ -905,6 +907,13 @@
             info.object = res.Get();
         }
 
+        if (auto* sem = program_->Sem().Get(expr)->As<sem::Load>()) {
+            auto* ref = sem->ReferenceType();
+            info.result_type = ref->StoreType()->Clone(clone_ctx_.type_ctx);
+        } else {
+            info.result_type = program_->Sem().Get(expr)->Type()->Clone(clone_ctx_.type_ctx);
+        }
+
         // The AST chain is `inside-out` compared to what we need, which means the list it generates
         // is backwards. We need to operate on the list in reverse order to have the correct access
         // chain.
@@ -912,10 +921,10 @@
             bool ok = tint::Switch(
                 accessor,
                 [&](const ast::IndexAccessorExpression* idx) {
-                    return GenerateIndexAccessor(idx, &info);
+                    return GenerateIndexAccessor(idx, info);
                 },
                 [&](const ast::MemberAccessorExpression* member) {
-                    return GenerateMemberAccessor(member, &info);
+                    return GenerateMemberAccessor(member, info);
                 },
                 [&](Default) {
                     TINT_ICE(Writer, diagnostics_)
@@ -927,49 +936,85 @@
             }
         }
 
-        const type::Type* ty = nullptr;
-        if (auto* sem = program_->Sem().Get(expr)->As<sem::Load>()) {
-            auto* ref = sem->ReferenceType();
-            ty = builder_.ir.Types().pointer(ref->StoreType()->Clone(clone_ctx_.type_ctx),
-                                             ref->AddressSpace(), ref->Access());
-        } else {
-            ty = program_->Sem().Get(expr)->UnwrapLoad()->Type()->Clone(clone_ctx_.type_ctx);
+        if (!info.indices.IsEmpty()) {
+            info.result = GenerateAccess(info);
         }
-
-        auto* access = builder_.Access(ty, info.object, info.indices);
-        current_block_->Append(access);
-        return access;
+        return info.result;
     }
 
-    bool GenerateIndexAccessor(const ast::IndexAccessorExpression* expr, AccessorInfo* info) {
+    Instruction* GenerateAccess(const AccessorInfo& info) {
+        // The access result type should match the source result type. If the source is a pointer,
+        // we generate a pointer.
+        const type::Type* ty = nullptr;
+        if (info.object->Type()->Is<type::Pointer>() && !info.result_type->Is<type::Pointer>()) {
+            auto* ptr = info.object->Type()->As<type::Pointer>();
+            ty = builder_.ir.Types().pointer(info.result_type, ptr->AddressSpace(), ptr->Access());
+        } else {
+            ty = info.result_type;
+        }
+
+        auto* a = builder_.Access(ty, info.object, info.indices);
+        current_block_->Append(a);
+        return a;
+    }
+
+    bool GenerateIndexAccessor(const ast::IndexAccessorExpression* expr, AccessorInfo& info) {
         auto res = EmitExpression(expr->index);
         if (!res) {
             return false;
         }
 
-        info->indices.Push(res.Get());
+        info.indices.Push(res.Get());
         return true;
     }
 
-    bool GenerateMemberAccessor(const ast::MemberAccessorExpression* expr, AccessorInfo* info) {
+    bool GenerateMemberAccessor(const ast::MemberAccessorExpression* expr, AccessorInfo& info) {
         auto* expr_sem = program_->Sem().Get(expr)->UnwrapLoad();
 
         return tint::Switch(
             expr_sem,  //
             [&](const sem::StructMemberAccess* access) {
                 uint32_t idx = access->Member()->Index();
-                info->indices.Push(builder_.Constant(u32(idx)));
+                info.indices.Push(builder_.Constant(u32(idx)));
                 return true;
             },
             [&](const sem::Swizzle* swizzle) {
                 auto& indices = swizzle->Indices();
+
+                // A single element swizzle is just treated as an accessor.
                 if (indices.Length() == 1) {
-                    info->indices.Push(builder_.Constant(u32(indices[0])));
+                    info.indices.Push(builder_.Constant(u32(indices[0])));
                     return true;
                 }
 
-                TINT_ICE(IR, diagnostics_) << "unhandled multi index swizzle";
-                return false;
+                // Store the result type away, this will be the result of the swizzle, but the
+                // intermediate steps need different result types.
+                auto* result_type = info.result_type;
+
+                // Emit any preceeding member/index accessors
+                if (!info.indices.IsEmpty()) {
+                    // The access chain is being split, the initial part of than will have a
+                    // resulting type that matches the object being swizzled.
+                    info.result_type = swizzle->Object()->Type()->Clone(clone_ctx_.type_ctx);
+                    info.object = GenerateAccess(info);
+                    info.indices.Clear();
+
+                    // If the sub-accessor generated a pointer result, make sure a load is emitted
+                    if (auto* ptr = info.object->Type()->As<type::Pointer>()) {
+                        auto* load = builder_.Load(info.object);
+                        info.result_type = ptr->StoreType();
+                        info.object = load;
+                        current_block_->Append(load);
+                    }
+                }
+
+                info.result = builder_.Swizzle(swizzle->Type()->Clone(clone_ctx_.type_ctx),
+                                               info.object, std::move(indices));
+                current_block_->Append(info.result);
+
+                info.object = info.result;
+                info.result_type = result_type;
+                return true;
             },
             [&](Default) {
                 TINT_ICE(IR, diagnostics_)
diff --git a/src/tint/ir/from_program_accessor_test.cc b/src/tint/ir/from_program_accessor_test.cc
index 1130c13..4318e94 100644
--- a/src/tint/ir/from_program_accessor_test.cc
+++ b/src/tint/ir/from_program_accessor_test.cc
@@ -193,7 +193,7 @@
 )");
 }
 
-TEST_F(IR_FromProgramAccessorTest, DISABLED_Accessor_Var_MultiElementSwizzle) {
+TEST_F(IR_FromProgramAccessorTest, Accessor_Var_MultiElementSwizzle) {
     // var a: vec3<f32>
     // let b = a.zyxz
 
@@ -216,7 +216,7 @@
 )");
 }
 
-TEST_F(IR_FromProgramAccessorTest, DISABLED_Accessor_Var_MultiElementSwizzleOfSwizzle) {
+TEST_F(IR_FromProgramAccessorTest, Accessor_Var_MultiElementSwizzleOfSwizzle) {
     // var a: vec3<f32>
     // let b = a.zyx.yy
 
@@ -232,15 +232,15 @@
   %b1 = block {
     %a:ptr<function, vec3<f32>, read_write> = var
     %3:vec3<f32> = load %a
-    %2:vec3<f32> = swizzle %1, zyx
-    %b:vec2<f32> = swizzle %2, yy
+    %4:vec3<f32> = swizzle %3, zyx
+    %b:vec2<f32> = swizzle %4, yy
     ret
   }
 }
 )");
 }
 
-TEST_F(IR_FromProgramAccessorTest, DISABLED_Accessor_Var_MultiElementSwizzle_MiddleOfChain) {
+TEST_F(IR_FromProgramAccessorTest, Accessor_Var_MultiElementSwizzle_MiddleOfChain) {
     // struct MyStruct { a: i32; foo: vec4<f32> }
     // var a: MyStruct;
     // let b = a.foo.zyx.yx[0]
@@ -262,11 +262,11 @@
               R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
   %b1 = block {
     %a:ptr<function, MyStruct, read_write> = var
-    %1:ptr<function, vec4<f32>, read_write> = access %a, 1
-    %2:vec4<f32> = load %1
-    %3:vec3<f32> = swizzle %2, zxy
-    %4:vec2<f32> = swizzle %3, yx
-    %b:f32 = access %4, 0
+    %3:ptr<function, vec4<f32>, read_write> = access %a 1u
+    %4:vec4<f32> = load %3
+    %5:vec3<f32> = swizzle %4, zyx
+    %6:vec2<f32> = swizzle %5, yx
+    %b:f32 = access %6 0u
     ret
   }
 }
@@ -424,7 +424,7 @@
 )");
 }
 
-TEST_F(IR_FromProgramAccessorTest, DISABLED_Accessor_Let_MultiElementSwizzle) {
+TEST_F(IR_FromProgramAccessorTest, Accessor_Let_MultiElementSwizzle) {
     // let a: vec3<f32 = vec3()>
     // let b = a.zyxz
 
@@ -438,15 +438,14 @@
     EXPECT_EQ(Disassemble(m.Get()),
               R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
   %b1 = block {
-    %1:vec3<f32> = construct vec3<f32>
-    %b:vec4<f32> = swizzle vec3<f32>(0u), zyxz
+    %b:vec4<f32> = swizzle vec3<f32>(0.0f), zyxz
     ret
   }
 }
 )");
 }
 
-TEST_F(IR_FromProgramAccessorTest, DISABLED_Accessor_Let_MultiElementSwizzleOfSwizzle) {
+TEST_F(IR_FromProgramAccessorTest, Accessor_Let_MultiElementSwizzleOfSwizzle) {
     // let a: vec3<f32> = vec3();
     // let b = a.zyx.yy
 
@@ -460,7 +459,7 @@
     EXPECT_EQ(Disassemble(m.Get()),
               R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
   %b1 = block {
-    %2:vec3<f32> = swizzle vec3<f32>(0u) zyx
+    %2:vec3<f32> = swizzle vec3<f32>(0.0f), zyx
     %b:vec2<f32> = swizzle %2, yy
     ret
   }
@@ -468,7 +467,7 @@
 )");
 }
 
-TEST_F(IR_FromProgramAccessorTest, DISABLED_Accessor_Let_MultiElementSwizzle_MiddleOfChain) {
+TEST_F(IR_FromProgramAccessorTest, Accessor_Let_MultiElementSwizzle_MiddleOfChain) {
     // struct MyStruct { a: i32; foo: vec4<f32> }
     // let a: MyStruct = MyStruct();
     // let b = a.foo.zyx.yx[0]
@@ -489,10 +488,10 @@
     EXPECT_EQ(Disassemble(m.Get()),
               R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
   %b1 = block {
-    %2:vec4<f32> = access MyStruct(), 1u
-    %2:vec3<f32> = swizzle %1, zxy
-    %3:vec2<f32> = swizzle %2, yx
-    %b:f32 = access %3, 0u
+    %2:vec4<f32> = access MyStruct(0i, vec4<f32>(0.0f)) 1u
+    %3:vec3<f32> = swizzle %2, zyx
+    %4:vec2<f32> = swizzle %3, yx
+    %b:f32 = access %4 0u
     ret
   }
 }
diff --git a/src/tint/ir/swizzle.cc b/src/tint/ir/swizzle.cc
new file mode 100644
index 0000000..1887d4d
--- /dev/null
+++ b/src/tint/ir/swizzle.cc
@@ -0,0 +1,32 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ir/swizzle.h"
+
+#include <utility>
+
+#include "src/tint/debug.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Swizzle);
+
+namespace tint::ir {
+
+Swizzle::Swizzle(const type::Type* ty, Value* object, utils::VectorRef<uint32_t> indices)
+    : result_type_(ty), object_(object), indices_(std::move(indices)) {
+    object_->AddUsage(this);
+}
+
+Swizzle::~Swizzle() = default;
+
+}  // namespace tint::ir
diff --git a/src/tint/ir/swizzle.h b/src/tint/ir/swizzle.h
new file mode 100644
index 0000000..31c3da1
--- /dev/null
+++ b/src/tint/ir/swizzle.h
@@ -0,0 +1,50 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_IR_SWIZZLE_H_
+#define SRC_TINT_IR_SWIZZLE_H_
+
+#include "src/tint/ir/instruction.h"
+#include "src/tint/utils/castable.h"
+
+namespace tint::ir {
+
+/// A swizzle instruction in the IR.
+class Swizzle : public utils::Castable<Swizzle, Instruction> {
+  public:
+    /// Constructor
+    /// @param result_type the result type
+    /// @param object the object being swizzled
+    /// @param indices the indices to swizzle
+    Swizzle(const type::Type* result_type, Value* object, utils::VectorRef<uint32_t> indices);
+    ~Swizzle() override;
+
+    /// @returns the type of the value
+    const type::Type* Type() const override { return result_type_; }
+
+    /// @returns the object used for the access
+    Value* Object() const { return object_; }
+
+    /// @returns the swizzle indices
+    utils::VectorRef<uint32_t> Indices() const { return indices_; }
+
+  private:
+    const type::Type* result_type_ = nullptr;
+    Value* object_ = nullptr;
+    utils::Vector<uint32_t, 4> indices_;
+};
+
+}  // namespace tint::ir
+
+#endif  // SRC_TINT_IR_SWIZZLE_H_