[msl] Delete AST backend

You will not be missed.

Bug: 388013849
Change-Id: Ia635c5dcc38ecc8025c8c117bcad53606278bd5c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/224519
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/cmd/loopy/main.cc b/src/tint/cmd/loopy/main.cc
index c47de84..57a90e5 100644
--- a/src/tint/cmd/loopy/main.cc
+++ b/src/tint/cmd/loopy/main.cc
@@ -270,14 +270,6 @@
         input_program = &*flattened;
     }
 
-    tint::msl::writer::Options gen_options;
-    gen_options.bindings = tint::msl::writer::GenerateBindings(program);
-    gen_options.array_length_from_uniform.ubo_binding = 30;
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 0},
-                                                                          0);
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 1},
-                                                                          1);
-
     // Convert the AST program to an IR module.
     auto ir = tint::wgsl::reader::ProgramToLoweredIR(*input_program);
     if (ir != tint::Success) {
@@ -285,6 +277,14 @@
         return false;
     }
 
+    tint::msl::writer::Options gen_options;
+    gen_options.bindings = tint::msl::writer::GenerateBindings(ir.Get());
+    gen_options.array_length_from_uniform.ubo_binding = 30;
+    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 0},
+                                                                          0);
+    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 1},
+                                                                          1);
+
     // Generate MSL from Tint IR.
     auto result = tint::msl::writer::Generate(ir.Get(), gen_options);
     if (result != tint::Success) {
diff --git a/src/tint/cmd/test/BUILD.bazel b/src/tint/cmd/test/BUILD.bazel
index d232be0..6b188d2 100644
--- a/src/tint/cmd/test/BUILD.bazel
+++ b/src/tint/cmd/test/BUILD.bazel
@@ -117,18 +117,12 @@
     "//conditions:default": [],
   }) + select({
     ":tint_build_msl_writer": [
-      "//src/tint/lang/msl/writer/ast_printer:test",
       "//src/tint/lang/msl/writer/common:test",
       "//src/tint/lang/msl/writer/raise:test",
       "//src/tint/lang/msl/writer:test",
     ],
     "//conditions:default": [],
   }) + select({
-    ":tint_build_msl_writer_and_tint_build_wgsl_reader_and_tint_build_wgsl_writer": [
-      "//src/tint/lang/msl/writer/ast_raise:test",
-    ],
-    "//conditions:default": [],
-  }) + select({
     ":tint_build_spv_reader": [
       "//src/tint/lang/spirv/reader/common:test",
       "//src/tint/lang/spirv/reader/parser:test",
@@ -260,14 +254,6 @@
     ],
 )
 selects.config_setting_group(
-    name = "tint_build_msl_writer_and_tint_build_wgsl_reader_and_tint_build_wgsl_writer",
-    match_all = [
-        ":tint_build_msl_writer",
-        ":tint_build_wgsl_reader",
-        ":tint_build_wgsl_writer",
-    ],
-)
-selects.config_setting_group(
     name = "tint_build_spv_reader_and_tint_build_wgsl_reader_and_tint_build_wgsl_writer",
     match_all = [
         ":tint_build_spv_reader",
diff --git a/src/tint/cmd/test/BUILD.cmake b/src/tint/cmd/test/BUILD.cmake
index 66d8c23..e5fa229 100644
--- a/src/tint/cmd/test/BUILD.cmake
+++ b/src/tint/cmd/test/BUILD.cmake
@@ -128,19 +128,12 @@
 
 if(TINT_BUILD_MSL_WRITER)
   tint_target_add_dependencies(tint_cmd_test_test_cmd test_cmd
-    tint_lang_msl_writer_ast_printer_test
     tint_lang_msl_writer_common_test
     tint_lang_msl_writer_raise_test
     tint_lang_msl_writer_test
   )
 endif(TINT_BUILD_MSL_WRITER)
 
-if(TINT_BUILD_MSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-  tint_target_add_dependencies(tint_cmd_test_test_cmd test_cmd
-    tint_lang_msl_writer_ast_raise_test
-  )
-endif(TINT_BUILD_MSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-
 if(TINT_BUILD_SPV_READER)
   tint_target_add_dependencies(tint_cmd_test_test_cmd test_cmd
     tint_lang_spirv_reader_common_test
diff --git a/src/tint/cmd/test/BUILD.gn b/src/tint/cmd/test/BUILD.gn
index 0b5a9b6..b767381 100644
--- a/src/tint/cmd/test/BUILD.gn
+++ b/src/tint/cmd/test/BUILD.gn
@@ -126,17 +126,11 @@
     if (tint_build_msl_writer) {
       deps += [
         "${tint_src_dir}/lang/msl/writer:unittests",
-        "${tint_src_dir}/lang/msl/writer/ast_printer:unittests",
         "${tint_src_dir}/lang/msl/writer/common:unittests",
         "${tint_src_dir}/lang/msl/writer/raise:unittests",
       ]
     }
 
-    if (tint_build_msl_writer && tint_build_wgsl_reader &&
-        tint_build_wgsl_writer) {
-      deps += [ "${tint_src_dir}/lang/msl/writer/ast_raise:unittests" ]
-    }
-
     if (tint_build_spv_reader) {
       deps += [
         "${tint_src_dir}/lang/spirv/reader:unittests",
diff --git a/src/tint/lang/msl/writer/BUILD.bazel b/src/tint/lang/msl/writer/BUILD.bazel
index d848197..de3d1ee 100644
--- a/src/tint/lang/msl/writer/BUILD.bazel
+++ b/src/tint/lang/msl/writer/BUILD.bazel
@@ -51,11 +51,6 @@
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/features",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/sem",
     "//src/tint/utils",
     "//src/tint/utils/containers",
     "//src/tint/utils/diagnostic",
@@ -70,7 +65,6 @@
     "//src/utils",
   ] + select({
     ":tint_build_msl_writer": [
-      "//src/tint/lang/msl/writer/ast_printer",
       "//src/tint/lang/msl/writer/common",
       "//src/tint/lang/msl/writer/printer",
       "//src/tint/lang/msl/writer/raise",
diff --git a/src/tint/lang/msl/writer/BUILD.cmake b/src/tint/lang/msl/writer/BUILD.cmake
index f986cbf..b26526b 100644
--- a/src/tint/lang/msl/writer/BUILD.cmake
+++ b/src/tint/lang/msl/writer/BUILD.cmake
@@ -34,8 +34,6 @@
 #                       Do not modify this file directly
 ################################################################################
 
-include(lang/msl/writer/ast_printer/BUILD.cmake)
-include(lang/msl/writer/ast_raise/BUILD.cmake)
 include(lang/msl/writer/common/BUILD.cmake)
 include(lang/msl/writer/helpers/BUILD.cmake)
 include(lang/msl/writer/printer/BUILD.cmake)
@@ -59,11 +57,6 @@
   tint_lang_core_constant
   tint_lang_core_ir
   tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_features
-  tint_lang_wgsl_program
-  tint_lang_wgsl_sem
   tint_utils
   tint_utils_containers
   tint_utils_diagnostic
@@ -83,7 +76,6 @@
 
 if(TINT_BUILD_MSL_WRITER)
   tint_target_add_dependencies(tint_lang_msl_writer lib
-    tint_lang_msl_writer_ast_printer
     tint_lang_msl_writer_common
     tint_lang_msl_writer_printer
     tint_lang_msl_writer_raise
@@ -221,11 +213,6 @@
   tint_lang_core_constant
   tint_lang_core_ir
   tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_features
-  tint_lang_wgsl_program
-  tint_lang_wgsl_sem
   tint_utils
   tint_utils_bytes
   tint_utils_containers
@@ -252,13 +239,4 @@
   )
 endif(TINT_BUILD_MSL_WRITER)
 
-if(TINT_BUILD_WGSL_READER)
-  tint_target_add_sources(tint_lang_msl_writer_fuzz fuzz
-    "lang/msl/writer/writer_ast_fuzz.cc"
-  )
-  tint_target_add_dependencies(tint_lang_msl_writer_fuzz fuzz
-    tint_cmd_fuzz_wgsl_fuzz
-  )
-endif(TINT_BUILD_WGSL_READER)
-
 endif(TINT_BUILD_MSL_WRITER)
\ No newline at end of file
diff --git a/src/tint/lang/msl/writer/BUILD.gn b/src/tint/lang/msl/writer/BUILD.gn
index 9028589..6ef418a 100644
--- a/src/tint/lang/msl/writer/BUILD.gn
+++ b/src/tint/lang/msl/writer/BUILD.gn
@@ -56,11 +56,6 @@
       "${tint_src_dir}/lang/core/constant",
       "${tint_src_dir}/lang/core/ir",
       "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/wgsl",
-      "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/features",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/sem",
       "${tint_src_dir}/utils",
       "${tint_src_dir}/utils/containers",
       "${tint_src_dir}/utils/diagnostic",
@@ -76,7 +71,6 @@
 
     if (tint_build_msl_writer) {
       deps += [
-        "${tint_src_dir}/lang/msl/writer/ast_printer",
         "${tint_src_dir}/lang/msl/writer/common",
         "${tint_src_dir}/lang/msl/writer/printer",
         "${tint_src_dir}/lang/msl/writer/raise",
@@ -194,11 +188,6 @@
       "${tint_src_dir}/lang/core/constant",
       "${tint_src_dir}/lang/core/ir",
       "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/wgsl",
-      "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/features",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/sem",
       "${tint_src_dir}/utils",
       "${tint_src_dir}/utils/bytes",
       "${tint_src_dir}/utils/containers",
@@ -220,10 +209,5 @@
         "${tint_src_dir}/lang/msl/writer/helpers",
       ]
     }
-
-    if (tint_build_wgsl_reader) {
-      sources += [ "writer_ast_fuzz.cc" ]
-      deps += [ "${tint_src_dir}/cmd/fuzz/wgsl:fuzz" ]
-    }
   }
 }
diff --git a/src/tint/lang/msl/writer/ast_printer/BUILD.bazel b/src/tint/lang/msl/writer/ast_printer/BUILD.bazel
deleted file mode 100644
index 8f47803..0000000
--- a/src/tint/lang/msl/writer/ast_printer/BUILD.bazel
+++ /dev/null
@@ -1,159 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.bazel.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-load("//src/tint:flags.bzl", "COPTS")
-load("@bazel_skylib//lib:selects.bzl", "selects")
-cc_library(
-  name = "ast_printer",
-  srcs = [
-    "ast_printer.cc",
-  ],
-  hdrs = [
-    "ast_printer.h",
-  ],
-  deps = [
-    "//src/tint/api/common",
-    "//src/tint/lang/core",
-    "//src/tint/lang/core/common",
-    "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/type",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast/transform",
-    "//src/tint/lang/wgsl/features",
-    "//src/tint/lang/wgsl/helpers",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/sem",
-    "//src/tint/utils",
-    "//src/tint/utils/containers",
-    "//src/tint/utils/diagnostic",
-    "//src/tint/utils/ice",
-    "//src/tint/utils/macros",
-    "//src/tint/utils/math",
-    "//src/tint/utils/memory",
-    "//src/tint/utils/result",
-    "//src/tint/utils/rtti",
-    "//src/tint/utils/symbol",
-    "//src/tint/utils/text",
-    "//src/utils",
-  ] + select({
-    ":tint_build_msl_writer": [
-      "//src/tint/lang/msl/writer/ast_raise",
-      "//src/tint/lang/msl/writer/common",
-    ],
-    "//conditions:default": [],
-  }),
-  copts = COPTS,
-  visibility = ["//visibility:public"],
-)
-cc_library(
-  name = "test",
-  alwayslink = True,
-  srcs = [
-    "array_accessor_test.cc",
-    "assign_test.cc",
-    "ast_function_test.cc",
-    "ast_printer_test.cc",
-    "ast_type_test.cc",
-    "binary_test.cc",
-    "bitcast_test.cc",
-    "block_test.cc",
-    "break_test.cc",
-    "builtin_test.cc",
-    "builtin_texture_test.cc",
-    "call_test.cc",
-    "case_test.cc",
-    "cast_test.cc",
-    "const_assert_test.cc",
-    "constructor_test.cc",
-    "continue_test.cc",
-    "discard_test.cc",
-    "helper_test.h",
-    "identifier_test.cc",
-    "if_test.cc",
-    "import_test.cc",
-    "loop_test.cc",
-    "member_accessor_test.cc",
-    "module_constant_test.cc",
-    "return_test.cc",
-    "sanitizer_test.cc",
-    "switch_test.cc",
-    "unary_op_test.cc",
-    "variable_decl_statement_test.cc",
-  ],
-  deps = [
-    "//src/tint/api/common",
-    "//src/tint/lang/core",
-    "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/type",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast:test",
-    "//src/tint/lang/wgsl/common",
-    "//src/tint/lang/wgsl/features",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/resolver",
-    "//src/tint/lang/wgsl/sem",
-    "//src/tint/utils",
-    "//src/tint/utils/containers",
-    "//src/tint/utils/diagnostic",
-    "//src/tint/utils/ice",
-    "//src/tint/utils/macros",
-    "//src/tint/utils/math",
-    "//src/tint/utils/memory",
-    "//src/tint/utils/result",
-    "//src/tint/utils/rtti",
-    "//src/tint/utils/symbol",
-    "//src/tint/utils/text",
-    "@gtest",
-    "//src/utils",
-  ] + select({
-    ":tint_build_msl_writer": [
-      "//src/tint/lang/msl/writer",
-      "//src/tint/lang/msl/writer/ast_printer",
-      "//src/tint/lang/msl/writer/common",
-    ],
-    "//conditions:default": [],
-  }),
-  copts = COPTS,
-  visibility = ["//visibility:public"],
-)
-
-alias(
-  name = "tint_build_msl_writer",
-  actual = "//src/tint:tint_build_msl_writer_true",
-)
-
diff --git a/src/tint/lang/msl/writer/ast_printer/BUILD.cfg b/src/tint/lang/msl/writer/ast_printer/BUILD.cfg
deleted file mode 100644
index 1c7e256..0000000
--- a/src/tint/lang/msl/writer/ast_printer/BUILD.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "condition": "tint_build_msl_writer"
-}
diff --git a/src/tint/lang/msl/writer/ast_printer/BUILD.cmake b/src/tint/lang/msl/writer/ast_printer/BUILD.cmake
deleted file mode 100644
index c88dba8..0000000
--- a/src/tint/lang/msl/writer/ast_printer/BUILD.cmake
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.cmake.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-if(TINT_BUILD_MSL_WRITER)
-################################################################################
-# Target:    tint_lang_msl_writer_ast_printer
-# Kind:      lib
-# Condition: TINT_BUILD_MSL_WRITER
-################################################################################
-tint_add_target(tint_lang_msl_writer_ast_printer lib
-  lang/msl/writer/ast_printer/ast_printer.cc
-  lang/msl/writer/ast_printer/ast_printer.h
-)
-
-tint_target_add_dependencies(tint_lang_msl_writer_ast_printer lib
-  tint_api_common
-  tint_lang_core
-  tint_lang_core_common
-  tint_lang_core_constant
-  tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_features
-  tint_lang_wgsl_helpers
-  tint_lang_wgsl_program
-  tint_lang_wgsl_sem
-  tint_utils
-  tint_utils_containers
-  tint_utils_diagnostic
-  tint_utils_ice
-  tint_utils_macros
-  tint_utils_math
-  tint_utils_memory
-  tint_utils_result
-  tint_utils_rtti
-  tint_utils_symbol
-  tint_utils_text
-)
-
-tint_target_add_external_dependencies(tint_lang_msl_writer_ast_printer lib
-  "src_utils"
-)
-
-if(TINT_BUILD_MSL_WRITER)
-  tint_target_add_dependencies(tint_lang_msl_writer_ast_printer lib
-    tint_lang_msl_writer_ast_raise
-    tint_lang_msl_writer_common
-  )
-endif(TINT_BUILD_MSL_WRITER)
-
-endif(TINT_BUILD_MSL_WRITER)
-if(TINT_BUILD_MSL_WRITER)
-################################################################################
-# Target:    tint_lang_msl_writer_ast_printer_test
-# Kind:      test
-# Condition: TINT_BUILD_MSL_WRITER
-################################################################################
-tint_add_target(tint_lang_msl_writer_ast_printer_test test
-  lang/msl/writer/ast_printer/array_accessor_test.cc
-  lang/msl/writer/ast_printer/assign_test.cc
-  lang/msl/writer/ast_printer/ast_function_test.cc
-  lang/msl/writer/ast_printer/ast_printer_test.cc
-  lang/msl/writer/ast_printer/ast_type_test.cc
-  lang/msl/writer/ast_printer/binary_test.cc
-  lang/msl/writer/ast_printer/bitcast_test.cc
-  lang/msl/writer/ast_printer/block_test.cc
-  lang/msl/writer/ast_printer/break_test.cc
-  lang/msl/writer/ast_printer/builtin_test.cc
-  lang/msl/writer/ast_printer/builtin_texture_test.cc
-  lang/msl/writer/ast_printer/call_test.cc
-  lang/msl/writer/ast_printer/case_test.cc
-  lang/msl/writer/ast_printer/cast_test.cc
-  lang/msl/writer/ast_printer/const_assert_test.cc
-  lang/msl/writer/ast_printer/constructor_test.cc
-  lang/msl/writer/ast_printer/continue_test.cc
-  lang/msl/writer/ast_printer/discard_test.cc
-  lang/msl/writer/ast_printer/helper_test.h
-  lang/msl/writer/ast_printer/identifier_test.cc
-  lang/msl/writer/ast_printer/if_test.cc
-  lang/msl/writer/ast_printer/import_test.cc
-  lang/msl/writer/ast_printer/loop_test.cc
-  lang/msl/writer/ast_printer/member_accessor_test.cc
-  lang/msl/writer/ast_printer/module_constant_test.cc
-  lang/msl/writer/ast_printer/return_test.cc
-  lang/msl/writer/ast_printer/sanitizer_test.cc
-  lang/msl/writer/ast_printer/switch_test.cc
-  lang/msl/writer/ast_printer/unary_op_test.cc
-  lang/msl/writer/ast_printer/variable_decl_statement_test.cc
-)
-
-tint_target_add_dependencies(tint_lang_msl_writer_ast_printer_test test
-  tint_api_common
-  tint_lang_core
-  tint_lang_core_constant
-  tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_test
-  tint_lang_wgsl_common
-  tint_lang_wgsl_features
-  tint_lang_wgsl_program
-  tint_lang_wgsl_resolver
-  tint_lang_wgsl_sem
-  tint_utils
-  tint_utils_containers
-  tint_utils_diagnostic
-  tint_utils_ice
-  tint_utils_macros
-  tint_utils_math
-  tint_utils_memory
-  tint_utils_result
-  tint_utils_rtti
-  tint_utils_symbol
-  tint_utils_text
-)
-
-tint_target_add_external_dependencies(tint_lang_msl_writer_ast_printer_test test
-  "gtest"
-  "src_utils"
-)
-
-if(TINT_BUILD_MSL_WRITER)
-  tint_target_add_dependencies(tint_lang_msl_writer_ast_printer_test test
-    tint_lang_msl_writer
-    tint_lang_msl_writer_ast_printer
-    tint_lang_msl_writer_common
-  )
-endif(TINT_BUILD_MSL_WRITER)
-
-endif(TINT_BUILD_MSL_WRITER)
\ No newline at end of file
diff --git a/src/tint/lang/msl/writer/ast_printer/BUILD.gn b/src/tint/lang/msl/writer/ast_printer/BUILD.gn
deleted file mode 100644
index 0590ba4..0000000
--- a/src/tint/lang/msl/writer/ast_printer/BUILD.gn
+++ /dev/null
@@ -1,158 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.gn.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-import("../../../../../../scripts/dawn_overrides_with_defaults.gni")
-import("../../../../../../scripts/tint_overrides_with_defaults.gni")
-
-import("${tint_src_dir}/tint.gni")
-
-if (tint_build_unittests || tint_build_benchmarks) {
-  import("//testing/test.gni")
-}
-if (tint_build_msl_writer) {
-  libtint_source_set("ast_printer") {
-    sources = [
-      "ast_printer.cc",
-      "ast_printer.h",
-    ]
-    deps = [
-      "${dawn_root}/src/utils:utils",
-      "${tint_src_dir}/api/common",
-      "${tint_src_dir}/lang/core",
-      "${tint_src_dir}/lang/core/common",
-      "${tint_src_dir}/lang/core/constant",
-      "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/wgsl",
-      "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/ast/transform",
-      "${tint_src_dir}/lang/wgsl/features",
-      "${tint_src_dir}/lang/wgsl/helpers",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/sem",
-      "${tint_src_dir}/utils",
-      "${tint_src_dir}/utils/containers",
-      "${tint_src_dir}/utils/diagnostic",
-      "${tint_src_dir}/utils/ice",
-      "${tint_src_dir}/utils/macros",
-      "${tint_src_dir}/utils/math",
-      "${tint_src_dir}/utils/memory",
-      "${tint_src_dir}/utils/result",
-      "${tint_src_dir}/utils/rtti",
-      "${tint_src_dir}/utils/symbol",
-      "${tint_src_dir}/utils/text",
-    ]
-
-    if (tint_build_msl_writer) {
-      deps += [
-        "${tint_src_dir}/lang/msl/writer/ast_raise",
-        "${tint_src_dir}/lang/msl/writer/common",
-      ]
-    }
-  }
-}
-if (tint_build_unittests) {
-  if (tint_build_msl_writer) {
-    tint_unittests_source_set("unittests") {
-      sources = [
-        "array_accessor_test.cc",
-        "assign_test.cc",
-        "ast_function_test.cc",
-        "ast_printer_test.cc",
-        "ast_type_test.cc",
-        "binary_test.cc",
-        "bitcast_test.cc",
-        "block_test.cc",
-        "break_test.cc",
-        "builtin_test.cc",
-        "builtin_texture_test.cc",
-        "call_test.cc",
-        "case_test.cc",
-        "cast_test.cc",
-        "const_assert_test.cc",
-        "constructor_test.cc",
-        "continue_test.cc",
-        "discard_test.cc",
-        "helper_test.h",
-        "identifier_test.cc",
-        "if_test.cc",
-        "import_test.cc",
-        "loop_test.cc",
-        "member_accessor_test.cc",
-        "module_constant_test.cc",
-        "return_test.cc",
-        "sanitizer_test.cc",
-        "switch_test.cc",
-        "unary_op_test.cc",
-        "variable_decl_statement_test.cc",
-      ]
-      deps = [
-        "${dawn_root}/src/utils:utils",
-        "${tint_src_dir}:gmock_and_gtest",
-        "${tint_src_dir}/api/common",
-        "${tint_src_dir}/lang/core",
-        "${tint_src_dir}/lang/core/constant",
-        "${tint_src_dir}/lang/core/type",
-        "${tint_src_dir}/lang/wgsl",
-        "${tint_src_dir}/lang/wgsl/ast",
-        "${tint_src_dir}/lang/wgsl/ast:unittests",
-        "${tint_src_dir}/lang/wgsl/common",
-        "${tint_src_dir}/lang/wgsl/features",
-        "${tint_src_dir}/lang/wgsl/program",
-        "${tint_src_dir}/lang/wgsl/resolver",
-        "${tint_src_dir}/lang/wgsl/sem",
-        "${tint_src_dir}/utils",
-        "${tint_src_dir}/utils/containers",
-        "${tint_src_dir}/utils/diagnostic",
-        "${tint_src_dir}/utils/ice",
-        "${tint_src_dir}/utils/macros",
-        "${tint_src_dir}/utils/math",
-        "${tint_src_dir}/utils/memory",
-        "${tint_src_dir}/utils/result",
-        "${tint_src_dir}/utils/rtti",
-        "${tint_src_dir}/utils/symbol",
-        "${tint_src_dir}/utils/text",
-      ]
-
-      if (tint_build_msl_writer) {
-        deps += [
-          "${tint_src_dir}/lang/msl/writer",
-          "${tint_src_dir}/lang/msl/writer/ast_printer",
-          "${tint_src_dir}/lang/msl/writer/common",
-        ]
-      }
-    }
-  }
-}
diff --git a/src/tint/lang/msl/writer/ast_printer/array_accessor_test.cc b/src/tint/lang/msl/writer/ast_printer/array_accessor_test.cc
deleted file mode 100644
index 4d8f4e5..0000000
--- a/src/tint/lang/msl/writer/ast_printer/array_accessor_test.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, IndexAccessor) {
-    auto* ary = Var("ary", ty.array<i32, 10>());
-    auto* expr = IndexAccessor("ary", 5_i);
-    WrapInFunction(ary, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "ary[5]");
-}
-
-TEST_F(MslASTPrinterTest, IndexAccessor_OfDref) {
-    GlobalVar("ary", ty.array<i32, 10>(), core::AddressSpace::kPrivate);
-
-    auto* p = Let("p", AddressOf("ary"));
-    auto* expr = IndexAccessor(Deref("p"), 5_i);
-    WrapInFunction(p, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(*(p))[5]");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/assign_test.cc b/src/tint/lang/msl/writer/ast_printer/assign_test.cc
deleted file mode 100644
index 256ec61..0000000
--- a/src/tint/lang/msl/writer/ast_printer/assign_test.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_Assign) {
-    auto* lhs = Var("lhs", ty.i32());
-    auto* rhs = Var("rhs", ty.i32());
-    auto* assign = Assign(lhs, rhs);
-    WrapInFunction(lhs, rhs, assign);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(assign)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  lhs = rhs;\n");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_function_test.cc b/src/tint/lang/msl/writer/ast_printer/ast_function_test.cc
deleted file mode 100644
index c8a5fdd..0000000
--- a/src/tint/lang/msl/writer/ast_printer/ast_function_test.cc
+++ /dev/null
@@ -1,726 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_Function) {
-    Func("my_func", tint::Empty, ty.void_(),
-         Vector{
-             Return(),
-         });
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  #include <metal_stdlib>
-
-  using namespace metal;
-  void my_func() {
-    return;
-  }
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Function_WithParams) {
-    Func("my_func",
-         Vector{
-             Param("a", ty.f32()),
-             Param("b", ty.i32()),
-         },
-         ty.void_(),
-         Vector{
-             Return(),
-         });
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  #include <metal_stdlib>
-
-  using namespace metal;
-  void my_func(float a, int b) {
-    return;
-  }
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Attribute_EntryPoint_NoReturn_Void) {
-    Func("main", tint::Empty, ty.void_(), {/* no explicit return */},
-         Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-fragment void main() {
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Attribute_EntryPoint_WithInOutVars) {
-    // fn frag_main(@location(0) foo : f32) -> @location(1) f32 {
-    //   return foo;
-    // }
-    auto* foo_in = Param("foo", ty.f32(), Vector{Location(0_a)});
-    Func("frag_main", Vector{foo_in}, ty.f32(),
-         Vector{
-             Return("foo"),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         },
-         Vector{
-             Location(1_a),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct tint_symbol_1 {
-  float foo [[user(locn0)]];
-};
-
-struct tint_symbol_2 {
-  float value [[color(1)]];
-};
-
-float frag_main_inner(float foo) {
-  return foo;
-}
-
-fragment tint_symbol_2 frag_main(tint_symbol_1 tint_symbol [[stage_in]]) {
-  float const inner_result = frag_main_inner(tint_symbol.foo);
-  tint_symbol_2 wrapper_result = {};
-  wrapper_result.value = inner_result;
-  return wrapper_result;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Attribute_EntryPoint_WithInOut_Builtins) {
-    // fn frag_main(@position(0) coord : vec4<f32>) -> @frag_depth f32 {
-    //   return coord.x;
-    // }
-    auto* coord_in = Param("coord", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kPosition)});
-    Func("frag_main", Vector{coord_in}, ty.f32(),
-         Vector{
-             Return(MemberAccessor("coord", "x")),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         },
-         Vector{
-             Builtin(core::BuiltinValue::kFragDepth),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct tint_symbol {
-  float value [[depth(any)]];
-};
-
-float frag_main_inner(float4 coord) {
-  return coord[0];
-}
-
-fragment tint_symbol frag_main(float4 coord [[position]]) {
-  float const inner_result = frag_main_inner(coord);
-  tint_symbol wrapper_result = {};
-  wrapper_result.value = inner_result;
-  return wrapper_result;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Attribute_EntryPoint_SharedStruct_DifferentStages) {
-    // struct Interface {
-    //   @location(1) col1 : f32;
-    //   @location(2) col2 : f32;
-    //   @builtin(position) pos : vec4<f32>;
-    // };
-    // fn vert_main() -> Interface {
-    //   return Interface(0.5, 0.25, vec4<f32>());
-    // }
-    // fn frag_main(colors : Interface) {
-    //   const r = colors.col1;
-    //   const g = colors.col2;
-    // }
-    auto* interface_struct =
-        Structure("Interface",
-                  Vector{
-                      Member("col1", ty.f32(), Vector{Location(1_a)}),
-                      Member("col2", ty.f32(), Vector{Location(2_a)}),
-                      Member("pos", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kPosition)}),
-                  });
-
-    Func("vert_main", tint::Empty, ty.Of(interface_struct),
-         Vector{Return(Call(ty.Of(interface_struct), 0.5_f, 0.25_f, Call<vec4<f32>>()))},
-         Vector{Stage(ast::PipelineStage::kVertex)});
-
-    Func("frag_main", Vector{Param("colors", ty.Of(interface_struct))}, ty.void_(),
-         Vector{
-             WrapInStatement(Let("r", ty.f32(), MemberAccessor("colors", "col1"))),
-             WrapInStatement(Let("g", ty.f32(), MemberAccessor("colors", "col2"))),
-         },
-         Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct Interface {
-  float col1;
-  float col2;
-  float4 pos;
-};
-
-struct tint_symbol {
-  float col1 [[user(locn1)]];
-  float col2 [[user(locn2)]];
-  float4 pos [[position]];
-};
-
-Interface vert_main_inner() {
-  Interface const tint_symbol_3 = Interface{.col1=0.5f, .col2=0.25f, .pos=float4(0.0f)};
-  return tint_symbol_3;
-}
-
-vertex tint_symbol vert_main() {
-  Interface const inner_result = vert_main_inner();
-  tint_symbol wrapper_result = {};
-  wrapper_result.col1 = inner_result.col1;
-  wrapper_result.col2 = inner_result.col2;
-  wrapper_result.pos = inner_result.pos;
-  return wrapper_result;
-}
-
-struct tint_symbol_2 {
-  float col1 [[user(locn1)]];
-  float col2 [[user(locn2)]];
-};
-
-void frag_main_inner(Interface colors) {
-  float const r = colors.col1;
-  float const g = colors.col2;
-}
-
-fragment void frag_main(float4 pos [[position]], tint_symbol_2 tint_symbol_1 [[stage_in]]) {
-  Interface const tint_symbol_4 = Interface{.col1=tint_symbol_1.col1, .col2=tint_symbol_1.col2, .pos=pos};
-  frag_main_inner(tint_symbol_4);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Attribute_EntryPoint_SharedStruct_HelperFunction) {
-    // struct VertexOutput {
-    //   @builtin(position) pos : vec4<f32>;
-    // };
-    // fn foo(x : f32) -> VertexOutput {
-    //   return VertexOutput(vec4<f32>(x, x, x, 1.0));
-    // }
-    // fn vert_main1() -> VertexOutput {
-    //   return foo(0.5);
-    // }
-    // fn vert_main2() -> VertexOutput {
-    //   return foo(0.25);
-    // }
-    auto* vertex_output_struct =
-        Structure("VertexOutput",
-                  Vector{
-                      Member("pos", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kPosition)}),
-                  });
-
-    Func("foo", Vector{Param("x", ty.f32())}, ty.Of(vertex_output_struct),
-         Vector{
-             Return(Call(ty.Of(vertex_output_struct), Call<vec4<f32>>("x", "x", "x", 1_f))),
-         });
-    Func("vert_main1", tint::Empty, ty.Of(vertex_output_struct),
-         Vector{Return(Expr(Call("foo", Expr(0.5_f))))},
-         Vector{Stage(ast::PipelineStage::kVertex)});
-
-    Func("vert_main2", tint::Empty, ty.Of(vertex_output_struct),
-         Vector{Return(Expr(Call("foo", Expr(0.25_f))))},
-         Vector{Stage(ast::PipelineStage::kVertex)});
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct VertexOutput {
-  float4 pos;
-};
-
-VertexOutput foo(float x) {
-  VertexOutput const tint_symbol_2 = VertexOutput{.pos=float4(x, x, x, 1.0f)};
-  return tint_symbol_2;
-}
-
-struct tint_symbol {
-  float4 pos [[position]];
-};
-
-VertexOutput vert_main1_inner() {
-  return foo(0.5f);
-}
-
-vertex tint_symbol vert_main1() {
-  VertexOutput const inner_result = vert_main1_inner();
-  tint_symbol wrapper_result = {};
-  wrapper_result.pos = inner_result.pos;
-  return wrapper_result;
-}
-
-struct tint_symbol_1 {
-  float4 pos [[position]];
-};
-
-VertexOutput vert_main2_inner() {
-  return foo(0.25f);
-}
-
-vertex tint_symbol_1 vert_main2() {
-  VertexOutput const inner_result_1 = vert_main2_inner();
-  tint_symbol_1 wrapper_result_1 = {};
-  wrapper_result_1.pos = inner_result_1.pos;
-  return wrapper_result_1;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_FunctionAttribute_EntryPoint_With_RW_StorageBuffer) {
-    auto* s = Structure("Data", Vector{
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.f32()),
-                                });
-
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite, Group(0_a),
-              Binding(0_a));
-
-    auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct Data {
-  /* 0x0000 */ int a;
-  /* 0x0004 */ float b;
-};
-
-fragment void frag_main(device Data* tint_symbol [[buffer(0)]]) {
-  float v = (*(tint_symbol)).b;
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_FunctionAttribute_EntryPoint_With_RO_StorageBuffer) {
-    auto* s = Structure("Data", Vector{
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.f32()),
-                                });
-
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Group(0_a),
-              Binding(0_a));
-
-    auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct Data {
-  /* 0x0000 */ int a;
-  /* 0x0004 */ float b;
-};
-
-fragment void frag_main(const device Data* tint_symbol [[buffer(0)]]) {
-  float v = (*(tint_symbol)).b;
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
-    auto* ubo_ty = Structure("UBO", Vector{Member("coord", ty.vec4<f32>())});
-    auto* ubo =
-        GlobalVar("ubo", ty.Of(ubo_ty), core::AddressSpace::kUniform, Group(0_a), Binding(0_a));
-
-    Func("sub_func",
-         Vector{
-             Param("param", ty.f32()),
-         },
-         ty.f32(),
-         Vector{
-             Return(MemberAccessor(MemberAccessor(ubo, "coord"), "x")),
-         });
-
-    auto* var = Var("v", ty.f32(), Call("sub_func", 1_f));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct UBO {
-  /* 0x0000 */ float4 coord;
-};
-
-float sub_func(float param, const constant UBO* const tint_symbol) {
-  return (*(tint_symbol)).coord[0];
-}
-
-fragment void frag_main(const constant UBO* tint_symbol_1 [[buffer(0)]]) {
-  float v = sub_func(1.0f, tint_symbol_1);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_FunctionAttribute_Called_By_EntryPoint_With_RW_StorageBuffer) {
-    auto* s = Structure("Data", Vector{
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.f32()),
-                                });
-
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite, Group(0_a),
-              Binding(0_a));
-
-    Func("sub_func",
-         Vector{
-             Param("param", ty.f32()),
-         },
-         ty.f32(),
-         Vector{
-             Return(MemberAccessor("coord", "b")),
-         });
-
-    auto* var = Var("v", ty.f32(), Call("sub_func", 1_f));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct Data {
-  /* 0x0000 */ int a;
-  /* 0x0004 */ float b;
-};
-
-float sub_func(float param, device Data* const tint_symbol) {
-  return (*(tint_symbol)).b;
-}
-
-fragment void frag_main(device Data* tint_symbol_1 [[buffer(0)]]) {
-  float v = sub_func(1.0f, tint_symbol_1);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_FunctionAttribute_Called_By_EntryPoint_With_RO_StorageBuffer) {
-    auto* s = Structure("Data", Vector{
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.f32()),
-                                });
-
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Group(0_a),
-              Binding(0_a));
-
-    Func("sub_func",
-         Vector{
-             Param("param", ty.f32()),
-         },
-         ty.f32(),
-         Vector{
-             Return(MemberAccessor("coord", "b")),
-         });
-
-    auto* var = Var("v", ty.f32(), Call("sub_func", 1_f));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct Data {
-  /* 0x0000 */ int a;
-  /* 0x0004 */ float b;
-};
-
-float sub_func(float param, const device Data* const tint_symbol) {
-  return (*(tint_symbol)).b;
-}
-
-fragment void frag_main(const device Data* tint_symbol_1 [[buffer(0)]]) {
-  float v = sub_func(1.0f, tint_symbol_1);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Function_WithArrayParams) {
-    Func("my_func",
-         Vector{
-             Param("a", ty.array<f32, 5>()),
-         },
-         ty.void_(),
-         Vector{
-             Return(),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  #include <metal_stdlib>
-
-  using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-  void my_func(tint_array<float, 5> a) {
-    return;
-  }
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Function_WithArrayReturn) {
-    Func("my_func", tint::Empty, ty.array<f32, 5>(),
-         Vector{
-             Return(Call(ty.array<f32, 5>())),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  #include <metal_stdlib>
-
-  using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-  tint_array<float, 5> my_func() {
-    tint_array<float, 5> const tint_symbol = tint_array<float, 5>{};
-    return tint_symbol;
-  }
-
-)");
-}
-
-// https://crbug.com/tint/297
-TEST_F(MslASTPrinterTest, Emit_Function_Multiple_EntryPoint_With_Same_ModuleVar) {
-    // struct Data {
-    //   d : f32;
-    // };
-    // @binding(0) @group(0) var<storage> data : Data;
-    //
-    // @compute @workgroup_size(1)
-    // fn a() {
-    //   return;
-    // }
-    //
-    // @compute @workgroup_size(1)
-    // fn b() {
-    //   return;
-    // }
-
-    auto* s = Structure("Data", Vector{Member("d", ty.f32())});
-
-    GlobalVar("data", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite, Group(0_a),
-              Binding(0_a));
-
-    {
-        auto* var = Var("v", ty.f32(), MemberAccessor("data", "d"));
-
-        Func("a", tint::Empty, ty.void_(),
-             Vector{
-                 Decl(var),
-                 Return(),
-             },
-             Vector{
-                 Stage(ast::PipelineStage::kCompute),
-                 WorkgroupSize(1_i),
-             });
-    }
-
-    {
-        auto* var = Var("v", ty.f32(), MemberAccessor("data", "d"));
-
-        Func("b", tint::Empty, ty.void_(),
-             Vector{
-                 Decl(var),
-                 Return(),
-             },
-             Vector{
-                 Stage(ast::PipelineStage::kCompute),
-                 WorkgroupSize(1_i),
-             });
-    }
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct Data {
-  /* 0x0000 */ float d;
-};
-
-kernel void a(device Data* tint_symbol [[buffer(0)]]) {
-  float v = (*(tint_symbol)).d;
-  return;
-}
-
-kernel void b(device Data* tint_symbol_1 [[buffer(0)]]) {
-  float v = (*(tint_symbol_1)).d;
-  return;
-}
-
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
deleted file mode 100644
index 39c1e69..0000000
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
+++ /dev/null
@@ -1,3378 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/ast_printer.h"
-
-#include <cmath>
-#include <iomanip>
-#include <limits>
-#include <utility>
-#include <vector>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/core/constant/splat.h"
-#include "src/tint/lang/core/constant/value.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/array.h"
-#include "src/tint/lang/core/type/atomic.h"
-#include "src/tint/lang/core/type/bool.h"
-#include "src/tint/lang/core/type/depth_multisampled_texture.h"
-#include "src/tint/lang/core/type/depth_texture.h"
-#include "src/tint/lang/core/type/f16.h"
-#include "src/tint/lang/core/type/f32.h"
-#include "src/tint/lang/core/type/i32.h"
-#include "src/tint/lang/core/type/matrix.h"
-#include "src/tint/lang/core/type/multisampled_texture.h"
-#include "src/tint/lang/core/type/pointer.h"
-#include "src/tint/lang/core/type/sampled_texture.h"
-#include "src/tint/lang/core/type/storage_texture.h"
-#include "src/tint/lang/core/type/texture_dimension.h"
-#include "src/tint/lang/core/type/u32.h"
-#include "src/tint/lang/core/type/vector.h"
-#include "src/tint/lang/core/type/void.h"
-#include "src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.h"
-#include "src/tint/lang/msl/writer/ast_raise/packed_vec3.h"
-#include "src/tint/lang/msl/writer/ast_raise/pixel_local.h"
-#include "src/tint/lang/msl/writer/ast_raise/quad_swap.h"
-#include "src/tint/lang/msl/writer/ast_raise/subgroup_ballot.h"
-#include "src/tint/lang/msl/writer/common/option_helpers.h"
-#include "src/tint/lang/msl/writer/common/printer_support.h"
-#include "src/tint/lang/wgsl/ast/alias.h"
-#include "src/tint/lang/wgsl/ast/bool_literal_expression.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/float_literal_expression.h"
-#include "src/tint/lang/wgsl/ast/interpolate_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h"
-#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
-#include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
-#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
-#include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
-#include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
-#include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h"
-#include "src/tint/lang/wgsl/ast/transform/fold_constants.h"
-#include "src/tint/lang/wgsl/ast/transform/manager.h"
-#include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h"
-#include "src/tint/lang/wgsl/ast/transform/preserve_padding.h"
-#include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h"
-#include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h"
-#include "src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h"
-#include "src/tint/lang/wgsl/ast/transform/remove_phonies.h"
-#include "src/tint/lang/wgsl/ast/transform/robustness.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
-#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
-#include "src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
-#include "src/tint/lang/wgsl/ast/transform/vertex_pulling.h"
-#include "src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-#include "src/tint/lang/wgsl/helpers/check_supported_extensions.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/module.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/lang/wgsl/sem/switch_statement.h"
-#include "src/tint/lang/wgsl/sem/value_constructor.h"
-#include "src/tint/lang/wgsl/sem/value_conversion.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/macros/defer.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-#include "src/tint/utils/rtti/switch.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-bool last_is_break(const ast::BlockStatement* stmts) {
-    return tint::IsAnyOf<ast::BreakStatement>(stmts->Last());
-}
-
-class ScopedBitCast {
-  public:
-    ScopedBitCast(ASTPrinter* generator,
-                  StringStream& stream,
-                  const core::type::Type* curr_type,
-                  const core::type::Type* target_type)
-        : s(stream) {
-        auto* target_vec_type = target_type->As<core::type::Vector>();
-
-        // If we need to promote from scalar to vector, bitcast the scalar to the
-        // vector element type.
-        if (curr_type->Is<core::type::Scalar>() && target_vec_type) {
-            target_type = target_vec_type->Type();
-        }
-
-        // Bit cast
-        s << "as_type<";
-        generator->EmitType(s, target_type);
-        s << ">(";
-    }
-
-    ~ScopedBitCast() { s << ")"; }
-
-  private:
-    StringStream& s;
-};
-
-}  // namespace
-
-SanitizedResult::SanitizedResult() = default;
-SanitizedResult::~SanitizedResult() = default;
-SanitizedResult::SanitizedResult(SanitizedResult&&) = default;
-
-SanitizedResult Sanitize(const Program& in, const Options& options) {
-    ast::transform::Manager manager;
-    ast::transform::DataMap data;
-
-    manager.Add<ast::transform::FoldConstants>();
-
-    manager.Add<ast::transform::DisableUniformityAnalysis>();
-
-    // ExpandCompoundAssignment must come before BuiltinPolyfill
-    manager.Add<ast::transform::ExpandCompoundAssignment>();
-
-    manager.Add<ast::transform::PreservePadding>();
-
-    manager.Add<ast::transform::Unshadow>();
-
-    manager.Add<ast::transform::PromoteSideEffectsToDecl>();
-
-    // VertexPulling must come before Robustness.
-    if (options.vertex_pulling_config) {
-        ast::transform::VertexPulling::Config config;
-        config.pulling_group = options.vertex_pulling_config->pulling_group;
-        config.vertex_state = options.vertex_pulling_config->vertex_state;
-        manager.Add<ast::transform::VertexPulling>();
-        data.Add<ast::transform::VertexPulling::Config>(std::move(config));
-    }
-
-    if (!options.disable_robustness) {
-        // Robustness must come after PromoteSideEffectsToDecl
-        // Robustness must come before BuiltinPolyfill and CanonicalizeEntryPointIO
-        // Robustness must come before ArrayLengthFromUniform
-        manager.Add<ast::transform::Robustness>();
-    }
-
-    tint::transform::multiplanar::BindingsMap multiplanar_map{};
-    RemapperData remapper_data{};
-    ArrayLengthFromUniformOptions array_length_from_uniform_options{};
-    PopulateBindingRelatedOptions(options, remapper_data, multiplanar_map,
-                                  array_length_from_uniform_options);
-
-    manager.Add<ast::transform::BindingRemapper>();
-    data.Add<ast::transform::BindingRemapper::Remappings>(
-        remapper_data, std::unordered_map<BindingPoint, core::Access>{},
-        /* allow_collisions */ true);
-
-    // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
-    // MultiplanarExternalTexture must come after BindingRemapper
-    data.Add<ast::transform::MultiplanarExternalTexture::NewBindingPoints>(
-        multiplanar_map, /* allow_collisions */ true);
-    manager.Add<ast::transform::MultiplanarExternalTexture>();
-
-    {  // Builtin polyfills
-        ast::transform::BuiltinPolyfill::Builtins polyfills;
-        polyfills.acosh = ast::transform::BuiltinPolyfill::Level::kRangeCheck;
-        polyfills.atanh = ast::transform::BuiltinPolyfill::Level::kRangeCheck;
-        polyfills.bitshift_modulo = true;  // crbug.com/tint/1543
-        polyfills.clamp_int = true;
-        polyfills.conv_f32_to_iu32 = true;
-        polyfills.extract_bits = ast::transform::BuiltinPolyfill::Level::kClampParameters;
-        polyfills.first_leading_bit = true;
-        polyfills.first_trailing_bit = true;
-        polyfills.fwidth_fine = true;
-        polyfills.insert_bits = ast::transform::BuiltinPolyfill::Level::kClampParameters;
-        polyfills.int_div_mod = !options.disable_polyfill_integer_div_mod;
-        polyfills.sign_int = true;
-        polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
-        polyfills.workgroup_uniform_load = true;
-        polyfills.dot_4x8_packed = true;
-        polyfills.pack_unpack_4x8 = true;
-        polyfills.pack_4xu8_clamp = true;
-        data.Add<ast::transform::BuiltinPolyfill::Config>(polyfills);
-        manager.Add<ast::transform::BuiltinPolyfill>();
-    }
-
-    if (!options.disable_workgroup_init) {
-        // ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
-        // ZeroInitWorkgroupMemory may inject new builtin parameters.
-        manager.Add<ast::transform::ZeroInitWorkgroupMemory>();
-    }
-
-    {
-        PixelLocal::Config cfg;
-        for (auto it : options.pixel_local_attachments) {
-            cfg.attachments.Add(it.first, it.second);
-        }
-        data.Add<PixelLocal::Config>(cfg);
-        manager.Add<PixelLocal>();
-    }
-
-    // Build the configs for the internal CanonicalizeEntryPointIO transform.
-    auto entry_point_io_cfg = ast::transform::CanonicalizeEntryPointIO::Config(
-        ast::transform::CanonicalizeEntryPointIO::ShaderStyle::kMsl, options.fixed_sample_mask,
-        options.emit_vertex_point_size);
-    // CanonicalizeEntryPointIO must come after Robustness
-    manager.Add<ast::transform::CanonicalizeEntryPointIO>();
-    data.Add<ast::transform::CanonicalizeEntryPointIO::Config>(std::move(entry_point_io_cfg));
-
-    manager.Add<ast::transform::PromoteInitializersToLet>();
-    manager.Add<ast::transform::RemoveContinueInSwitch>();
-
-    // DemoteToHelper must come after PromoteSideEffectsToDecl and ExpandCompoundAssignment.
-    // TODO(crbug.com/tint/1752): This is only necessary for Metal versions older than 2.3.
-    manager.Add<ast::transform::DemoteToHelper>();
-
-    manager.Add<ast::transform::VectorizeScalarMatrixInitializers>();
-    manager.Add<ast::transform::RemovePhonies>();
-    manager.Add<ast::transform::SimplifyPointers>();
-
-    // SubgroupBallot() must come after CanonicalizeEntryPointIO.
-    manager.Add<SubgroupBallot>();
-
-    // QuadSwap() must come after CanonicalizeEntryPointIO.
-    manager.Add<QuadSwap>();
-
-    // ArrayLengthFromUniform must come after SimplifyPointers, as
-    // it assumes that the form of the array length argument is &var.array.
-    manager.Add<ast::transform::ArrayLengthFromUniform>();
-    // Build the config for the internal ArrayLengthFromUniform transform.
-    ast::transform::ArrayLengthFromUniform::Config array_length_from_uniform_cfg(
-        BindingPoint{0, array_length_from_uniform_options.ubo_binding});
-    array_length_from_uniform_cfg.bindpoint_to_size_index =
-        std::move(array_length_from_uniform_options.bindpoint_to_size_index);
-    data.Add<ast::transform::ArrayLengthFromUniform::Config>(
-        std::move(array_length_from_uniform_cfg));
-
-    // PackedVec3 must come after ExpandCompoundAssignment.
-    manager.Add<PackedVec3>();
-    manager.Add<ModuleScopeVarToEntryPointParam>();
-
-    SanitizedResult result;
-    ast::transform::DataMap outputs;
-    result.program = manager.Run(in, data, outputs);
-    if (!result.program.IsValid()) {
-        return result;
-    }
-    if (auto* res = outputs.Get<ast::transform::ArrayLengthFromUniform::Result>()) {
-        result.needs_storage_buffer_sizes = !res->used_size_indices.empty();
-    }
-    return result;
-}
-
-ASTPrinter::ASTPrinter(const Program& program, const Options& options)
-    : builder_(ProgramBuilder::Wrap(program)), options_(options) {}
-
-ASTPrinter::~ASTPrinter() = default;
-
-bool ASTPrinter::Generate() {
-    if (!tint::wgsl::CheckSupportedExtensions(
-            "MSL", builder_.AST(), diagnostics_,
-            Vector{
-                wgsl::Extension::kChromiumDisableUniformityAnalysis,
-                wgsl::Extension::kChromiumExperimentalFramebufferFetch,
-                wgsl::Extension::kChromiumExperimentalPixelLocal,
-                wgsl::Extension::kChromiumInternalGraphite,
-                wgsl::Extension::kChromiumInternalRelaxedUniformLayout,
-                wgsl::Extension::kClipDistances,
-                wgsl::Extension::kF16,
-                wgsl::Extension::kDualSourceBlending,
-                wgsl::Extension::kSubgroups,
-                wgsl::Extension::kSubgroupsF16,
-            })) {
-        return false;
-    }
-
-    Line() << "#include <metal_stdlib>";
-    Line();
-    Line() << "using namespace metal;";
-
-    auto helpers_insertion_point = current_buffer_->lines.size();
-
-    auto* mod = builder_.Sem().Module();
-    for (auto* decl : mod->DependencyOrderedDeclarations()) {
-        bool ok = Switch(
-            decl,  //
-            [&](const ast::Struct* str) {
-                TINT_DEFER(Line());
-                return EmitTypeDecl(TypeOf(str));
-            },
-            [&](const ast::Alias*) {
-                return true;  // folded away by the writer
-            },
-            [&](const ast::Const*) {
-                return true;  // Constants are embedded at their use
-            },
-            [&](const ast::Override*) {
-                // Override is removed with SubstituteOverride
-                diagnostics_.AddError(Source{})
-                    << "override-expressions should have been removed with the "
-                       "SubstituteOverride transform.";
-                return false;
-            },
-            [&](const ast::Function* func) {
-                TINT_DEFER(Line());
-                if (func->IsEntryPoint()) {
-                    return EmitEntryPointFunction(func);
-                }
-                return EmitFunction(func);
-            },
-            [&](const ast::DiagnosticDirective*) {
-                // Do nothing for diagnostic directives in MSL
-                return true;
-            },
-            [&](const ast::Enable*) {
-                // Do nothing for enabling extension in MSL
-                return true;
-            },
-            [&](const ast::Requires*) {
-                // Do nothing for requiring language features in MSL.
-                return true;
-            },
-            [&](const ast::ConstAssert*) {
-                return true;  // Not emitted
-            },                //
-            TINT_ICE_ON_NO_MATCH);
-        if (!ok) {
-            return false;
-        }
-    }
-
-    if (!invariant_define_name_.empty()) {
-        // 'invariant' attribute requires MSL 2.1 or higher.
-        // WGSL can ignore the invariant attribute on pre MSL 2.1 devices.
-        // See: https://github.com/gpuweb/gpuweb/issues/893#issuecomment-745537465
-        Line(&helpers_) << "#if __METAL_VERSION__ >= 210";
-        Line(&helpers_) << "#define " << invariant_define_name_ << " [[invariant]]";
-        Line(&helpers_) << "#else";
-        Line(&helpers_) << "#define " << invariant_define_name_;
-        Line(&helpers_) << "#endif";
-        Line(&helpers_);
-    }
-
-    if (!helpers_.lines.empty()) {
-        current_buffer_->Insert("", helpers_insertion_point++, 0);
-        current_buffer_->Insert(helpers_, helpers_insertion_point++, 0);
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitTypeDecl(const core::type::Type* ty) {
-    if (auto* str = ty->As<core::type::Struct>()) {
-        if (!EmitStructType(current_buffer_, str)) {
-            return false;
-        }
-    } else {
-        diagnostics_.AddError(Source{}) << "unknown alias type: " << ty->FriendlyName();
-        return false;
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitIndexAccessor(StringStream& out, const ast::IndexAccessorExpression* expr) {
-    bool paren_lhs =
-        !expr->object
-             ->IsAnyOf<ast::AccessorExpression, ast::CallExpression, ast::IdentifierExpression>();
-
-    if (paren_lhs) {
-        out << "(";
-    }
-    if (!EmitExpression(out, expr->object)) {
-        return false;
-    }
-    if (paren_lhs) {
-        out << ")";
-    }
-
-    out << "[";
-
-    if (!EmitExpression(out, expr->index)) {
-        return false;
-    }
-    out << "]";
-
-    return true;
-}
-
-bool ASTPrinter::EmitBitcastCall(StringStream& out, const ast::CallExpression* call) {
-    auto* arg = call->args[0];
-    auto* dst_type = TypeOf(call);
-
-    out << "as_type<";
-    if (!EmitType(out, dst_type)) {
-        return false;
-    }
-
-    out << ">(";
-    if (!EmitExpression(out, arg)) {
-        return false;
-    }
-
-    out << ")";
-    return true;
-}
-
-bool ASTPrinter::EmitAssign(const ast::AssignmentStatement* stmt) {
-    auto out = Line();
-
-    if (!EmitExpression(out, stmt->lhs)) {
-        return false;
-    }
-
-    out << " = ";
-
-    if (!EmitExpression(out, stmt->rhs)) {
-        return false;
-    }
-
-    out << ";";
-
-    return true;
-}
-
-bool ASTPrinter::EmitBinary(StringStream& out, const ast::BinaryExpression* expr) {
-    auto emit_op = [&] {
-        out << " ";
-
-        switch (expr->op) {
-            case core::BinaryOp::kAnd:
-                out << "&";
-                break;
-            case core::BinaryOp::kOr:
-                out << "|";
-                break;
-            case core::BinaryOp::kXor:
-                out << "^";
-                break;
-            case core::BinaryOp::kLogicalAnd:
-                out << "&&";
-                break;
-            case core::BinaryOp::kLogicalOr:
-                out << "||";
-                break;
-            case core::BinaryOp::kEqual:
-                out << "==";
-                break;
-            case core::BinaryOp::kNotEqual:
-                out << "!=";
-                break;
-            case core::BinaryOp::kLessThan:
-                out << "<";
-                break;
-            case core::BinaryOp::kGreaterThan:
-                out << ">";
-                break;
-            case core::BinaryOp::kLessThanEqual:
-                out << "<=";
-                break;
-            case core::BinaryOp::kGreaterThanEqual:
-                out << ">=";
-                break;
-            case core::BinaryOp::kShiftLeft:
-                out << "<<";
-                break;
-            case core::BinaryOp::kShiftRight:
-                // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
-                // implementation-defined behaviour for negative LHS.  We may have to
-                // generate extra code to implement WGSL-specified behaviour for
-                // negative LHS.
-                out << R"(>>)";
-                break;
-
-            case core::BinaryOp::kAdd:
-                out << "+";
-                break;
-            case core::BinaryOp::kSubtract:
-                out << "-";
-                break;
-            case core::BinaryOp::kMultiply:
-                out << "*";
-                break;
-            case core::BinaryOp::kDivide:
-                out << "/";
-                break;
-            case core::BinaryOp::kModulo:
-                out << "%";
-                break;
-        }
-        out << " ";
-        return true;
-    };
-
-    auto signed_type_of = [&](const core::type::Type* ty) -> const core::type::Type* {
-        if (ty->IsIntegerScalar()) {
-            return builder_.create<core::type::I32>();
-        } else if (auto* v = ty->As<core::type::Vector>()) {
-            return builder_.create<core::type::Vector>(builder_.create<core::type::I32>(),
-                                                       v->Width());
-        }
-        return {};
-    };
-
-    auto unsigned_type_of = [&](const core::type::Type* ty) -> const core::type::Type* {
-        if (ty->IsIntegerScalar()) {
-            return builder_.create<core::type::U32>();
-        } else if (auto* v = ty->As<core::type::Vector>()) {
-            return builder_.create<core::type::Vector>(builder_.create<core::type::U32>(),
-                                                       v->Width());
-        }
-        return {};
-    };
-
-    auto* lhs_type = TypeOf(expr->lhs)->UnwrapRef();
-    auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
-
-    // Handle fmod
-    if (expr->op == core::BinaryOp::kModulo && lhs_type->IsFloatScalarOrVector()) {
-        out << "fmod";
-        ScopedParen sp(out);
-        if (!EmitExpression(out, expr->lhs)) {
-            return false;
-        }
-        out << ", ";
-        if (!EmitExpression(out, expr->rhs)) {
-            return false;
-        }
-        return true;
-    }
-
-    // Handle +/-/* of signed values
-    if ((expr->IsAdd() || expr->IsSubtract() || expr->IsMultiply()) &&
-        lhs_type->IsSignedIntegerScalarOrVector() && rhs_type->IsSignedIntegerScalarOrVector()) {
-        // If lhs or rhs is a vector, use that type (support implicit scalar to
-        // vector promotion)
-        auto* target_type = lhs_type->Is<core::type::Vector>()
-                                ? lhs_type
-                                : (rhs_type->Is<core::type::Vector>() ? rhs_type : lhs_type);
-
-        // WGSL defines behaviour for signed overflow, MSL does not. For these
-        // cases, bitcast operands to unsigned, then cast result to signed.
-        ScopedBitCast outer_int_cast(this, out, target_type, signed_type_of(target_type));
-        ScopedParen sp(out);
-        {
-            ScopedBitCast lhs_uint_cast(this, out, lhs_type, unsigned_type_of(target_type));
-            if (!EmitExpression(out, expr->lhs)) {
-                return false;
-            }
-        }
-        if (!emit_op()) {
-            return false;
-        }
-        {
-            ScopedBitCast rhs_uint_cast(this, out, rhs_type, unsigned_type_of(target_type));
-            if (!EmitExpression(out, expr->rhs)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    // Handle left bit shifting a signed value
-    // TODO(crbug.com/tint/1077): This may not be necessary. The MSL spec
-    // seems to imply that left shifting a signed value is treated the same as
-    // left shifting an unsigned value, but we need to make sure.
-    if (expr->IsShiftLeft() && lhs_type->IsSignedIntegerScalarOrVector()) {
-        // Shift left: discards top bits, so convert first operand to unsigned
-        // first, then convert result back to signed
-        ScopedBitCast outer_int_cast(this, out, lhs_type, signed_type_of(lhs_type));
-        ScopedParen sp(out);
-        {
-            ScopedBitCast lhs_uint_cast(this, out, lhs_type, unsigned_type_of(lhs_type));
-            if (!EmitExpression(out, expr->lhs)) {
-                return false;
-            }
-        }
-        if (!emit_op()) {
-            return false;
-        }
-        if (!EmitExpression(out, expr->rhs)) {
-            return false;
-        }
-        return true;
-    }
-
-    // Handle '&' and '|' of booleans.
-    if ((expr->IsAnd() || expr->IsOr()) && lhs_type->Is<core::type::Bool>()) {
-        out << "bool";
-        ScopedParen sp(out);
-        if (!EmitExpression(out, expr->lhs)) {
-            return false;
-        }
-        if (!emit_op()) {
-            return false;
-        }
-        if (!EmitExpression(out, expr->rhs)) {
-            return false;
-        }
-        return true;
-    }
-
-    // Emit as usual
-    ScopedParen sp(out);
-    if (!EmitExpression(out, expr->lhs)) {
-        return false;
-    }
-    if (!emit_op()) {
-        return false;
-    }
-    if (!EmitExpression(out, expr->rhs)) {
-        return false;
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitBreak(const ast::BreakStatement*) {
-    Line() << "break;";
-    return true;
-}
-
-bool ASTPrinter::EmitBreakIf(const ast::BreakIfStatement* b) {
-    auto out = Line();
-    out << "if (";
-    if (!EmitExpression(out, b->condition)) {
-        return false;
-    }
-    out << ") { break; }";
-    return true;
-}
-
-bool ASTPrinter::EmitCall(StringStream& out, const ast::CallExpression* expr) {
-    auto* call = builder_.Sem().Get<sem::Call>(expr);
-    auto* target = call->Target();
-    return Switch(
-        target,  //
-        [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
-        [&](const sem::BuiltinFn* builtin) { return EmitBuiltinCall(out, call, builtin); },
-        [&](const sem::ValueConversion* conv) { return EmitTypeConversion(out, call, conv); },
-        [&](const sem::ValueConstructor* ctor) { return EmitTypeInitializer(out, call, ctor); },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitFunctionCall(StringStream& out,
-                                  const sem::Call* call,
-                                  const sem::Function* fn) {
-    if (ast::GetAttribute<SubgroupBallot::SimdBallot>(fn->Declaration()->attributes) != nullptr) {
-        out << "as_type<uint2>((ulong)simd_ballot(";
-        if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
-            return false;
-        }
-        out << "))";
-        return true;
-    }
-
-    if (ast::GetAttribute<QuadSwap::QuadShuffle>(fn->Declaration()->attributes) != nullptr) {
-        out << "quad_shuffle(";
-        if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
-            return false;
-        }
-        out << ",";
-        if (!EmitExpression(out, call->Arguments()[1]->Declaration())) {
-            return false;
-        }
-        out << ")";
-        return true;
-    }
-
-    out << fn->Declaration()->name->symbol.Name() << "(";
-
-    bool first = true;
-    for (auto* arg : call->Arguments()) {
-        if (!first) {
-            out << ", ";
-        }
-        first = false;
-
-        if (!EmitExpression(out, arg->Declaration())) {
-            return false;
-        }
-    }
-
-    out << ")";
-    return true;
-}
-
-bool ASTPrinter::EmitBuiltinCall(StringStream& out,
-                                 const sem::Call* call,
-                                 const sem::BuiltinFn* builtin) {
-    auto* expr = call->Declaration();
-    if (builtin->IsAtomic()) {
-        return EmitAtomicCall(out, expr, builtin);
-    }
-    if (builtin->IsTexture()) {
-        return EmitTextureCall(out, call, builtin);
-    }
-
-    auto name = generate_builtin_name(builtin);
-
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kBitcast:
-            return EmitBitcastCall(out, expr);
-        case wgsl::BuiltinFn::kDot:
-            return EmitDotCall(out, expr, builtin);
-        case wgsl::BuiltinFn::kModf:
-            return EmitModfCall(out, expr, builtin);
-        case wgsl::BuiltinFn::kFrexp:
-            return EmitFrexpCall(out, expr, builtin);
-        case wgsl::BuiltinFn::kDegrees:
-            return EmitDegreesCall(out, expr, builtin);
-        case wgsl::BuiltinFn::kRadians:
-            return EmitRadiansCall(out, expr, builtin);
-
-        case wgsl::BuiltinFn::kPack2X16Float:
-        case wgsl::BuiltinFn::kUnpack2X16Float: {
-            if (builtin->Fn() == wgsl::BuiltinFn::kPack2X16Float) {
-                out << "as_type<uint>(half2(";
-            } else {
-                out << "float2(as_type<half2>(";
-            }
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << "))";
-            return true;
-        }
-        case wgsl::BuiltinFn::kQuantizeToF16: {
-            std::string width = "";
-            if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
-                width = std::to_string(vec->Width());
-            }
-            out << "float" << width << "(half" << width << "(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << "))";
-            return true;
-        }
-        // TODO(crbug.com/tint/661): Combine sequential barriers to a single
-        // instruction.
-        case wgsl::BuiltinFn::kStorageBarrier: {
-            out << "threadgroup_barrier(mem_flags::mem_device)";
-            return true;
-        }
-        case wgsl::BuiltinFn::kWorkgroupBarrier: {
-            out << "threadgroup_barrier(mem_flags::mem_threadgroup)";
-            return true;
-        }
-        case wgsl::BuiltinFn::kTextureBarrier: {
-            out << "threadgroup_barrier(mem_flags::mem_texture)";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kLength: {
-            auto* sem = builder_.Sem().GetVal(expr->args[0]);
-            if (sem->Type()->UnwrapRef()->Is<core::type::Scalar>()) {
-                // Emulate scalar overload using fabs(x).
-                name = "fabs";
-            }
-            break;
-        }
-
-        case wgsl::BuiltinFn::kDistance: {
-            auto* sem = builder_.Sem().GetVal(expr->args[0]);
-            if (sem->Type()->UnwrapRef()->Is<core::type::Scalar>()) {
-                // Emulate scalar overload using fabs(x - y);
-                out << "fabs";
-                ScopedParen sp(out);
-                if (!EmitExpression(out, expr->args[0])) {
-                    return false;
-                }
-                out << " - ";
-                if (!EmitExpression(out, expr->args[1])) {
-                    return false;
-                }
-                return true;
-            }
-            break;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupElect: {
-            out << "simd_is_first()";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupBroadcast: {
-            // The lane argument is ushort.
-            out << "simd_broadcast(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ",ushort(";
-            if (!EmitExpression(out, expr->args[1])) {
-                return false;
-            }
-            out << "))";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupBroadcastFirst: {
-            out << "simd_broadcast_first(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupShuffle: {
-            out << "simd_shuffle(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ",";
-            if (!EmitExpression(out, expr->args[1])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupShuffleXor: {
-            out << "simd_shuffle_xor(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ",";
-            if (!EmitExpression(out, expr->args[1])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupShuffleUp: {
-            out << "simd_shuffle_up(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ",";
-            if (!EmitExpression(out, expr->args[1])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupShuffleDown: {
-            out << "simd_shuffle_down(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ",";
-            if (!EmitExpression(out, expr->args[1])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupAdd: {
-            out << "simd_sum(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupInclusiveAdd: {
-            out << "simd_prefix_inclusive_sum(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupExclusiveAdd: {
-            out << "simd_prefix_exclusive_sum(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupMul: {
-            out << "simd_product(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupInclusiveMul: {
-            out << "simd_prefix_inclusive_product(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupExclusiveMul: {
-            out << "simd_prefix_exclusive_product(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupAnd: {
-            out << "simd_and(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupOr: {
-            out << "simd_or(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupXor: {
-            out << "simd_xor(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupMin: {
-            out << "simd_min(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupMax: {
-            out << "simd_max(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupAll: {
-            out << "simd_all(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kSubgroupAny: {
-            out << "simd_any(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kQuadBroadcast: {
-            out << "quad_broadcast(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << ",";
-            if (!EmitExpression(out, expr->args[1])) {
-                return false;
-            }
-            out << ")";
-            return true;
-        }
-
-        default:
-            break;
-    }
-
-    if (name.empty()) {
-        return false;
-    }
-
-    out << name << "(";
-
-    bool first = true;
-    for (auto* arg : expr->args) {
-        if (!first) {
-            out << ", ";
-        }
-        first = false;
-
-        if (!EmitExpression(out, arg)) {
-            return false;
-        }
-    }
-
-    out << ")";
-    return true;
-}
-
-bool ASTPrinter::EmitTypeConversion(StringStream& out,
-                                    const sem::Call* call,
-                                    const sem::ValueConversion* conv) {
-    if (!EmitType(out, conv->Target())) {
-        return false;
-    }
-    out << "(";
-
-    if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
-        return false;
-    }
-
-    out << ")";
-    return true;
-}
-
-bool ASTPrinter::EmitTypeInitializer(StringStream& out,
-                                     const sem::Call* call,
-                                     const sem::ValueConstructor* ctor) {
-    auto* type = ctor->ReturnType();
-
-    const char* terminator = ")";
-    TINT_DEFER(out << terminator);
-
-    bool ok = Switch(
-        type,
-        [&](const core::type::Array*) {
-            if (!EmitType(out, type)) {
-                return false;
-            }
-            out << "{";
-            terminator = "}";
-            return true;
-        },
-        [&](const core::type::Struct*) {
-            if (!EmitType(out, type)) {
-                return false;
-            }
-            out << "{";
-            terminator = "}";
-            return true;
-        },
-        [&](Default) {
-            if (!EmitType(out, type)) {
-                return false;
-            }
-            out << "(";
-            return true;
-        });
-    if (!ok) {
-        return false;
-    }
-
-    size_t i = 0;
-    for (auto* arg : call->Arguments()) {
-        if (i > 0) {
-            out << ", ";
-        }
-
-        if (auto* struct_ty = type->As<core::type::Struct>()) {
-            // Emit field designators for structures to account for padding members.
-            auto name = struct_ty->Members()[i]->Name().Name();
-            out << "." << name << "=";
-        }
-
-        if (!EmitExpression(out, arg->Declaration())) {
-            return false;
-        }
-
-        i++;
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitAtomicCall(StringStream& out,
-                                const ast::CallExpression* expr,
-                                const sem::BuiltinFn* builtin) {
-    auto call = [&](const std::string& name, bool append_memory_order_relaxed) {
-        out << name;
-        {
-            ScopedParen sp(out);
-            for (size_t i = 0; i < expr->args.Length(); i++) {
-                auto* arg = expr->args[i];
-                if (i > 0) {
-                    out << ", ";
-                }
-                if (!EmitExpression(out, arg)) {
-                    return false;
-                }
-            }
-            if (append_memory_order_relaxed) {
-                out << ", memory_order_relaxed";
-            }
-        }
-        return true;
-    };
-
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kAtomicLoad:
-            return call("atomic_load_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicStore:
-            return call("atomic_store_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicAdd:
-            return call("atomic_fetch_add_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicSub:
-            return call("atomic_fetch_sub_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicMax:
-            return call("atomic_fetch_max_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicMin:
-            return call("atomic_fetch_min_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicAnd:
-            return call("atomic_fetch_and_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicOr:
-            return call("atomic_fetch_or_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicXor:
-            return call("atomic_fetch_xor_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicExchange:
-            return call("atomic_exchange_explicit", true);
-
-        case wgsl::BuiltinFn::kAtomicCompareExchangeWeak: {
-            auto* ptr_ty = TypeOf(expr->args[0])->UnwrapRef()->As<core::type::Pointer>();
-            auto sc = ptr_ty->AddressSpace();
-            auto* str = builtin->ReturnType()->As<core::type::Struct>();
-
-            auto func = tint::GetOrAdd(
-                atomicCompareExchangeWeak_, ACEWKeyType{{sc, str}}, [&]() -> std::string {
-                    if (!EmitStructType(&helpers_,
-                                        builtin->ReturnType()->As<core::type::Struct>())) {
-                        return "";
-                    }
-
-                    auto name = UniqueIdentifier("atomicCompareExchangeWeak");
-                    auto& buf = helpers_;
-                    auto* atomic_ty = builtin->Parameters()[0]->Type();
-                    auto* arg_ty = builtin->Parameters()[1]->Type();
-
-                    {
-                        auto f = Line(&buf);
-                        auto str_name = StructName(builtin->ReturnType()->As<core::type::Struct>());
-                        f << str_name << " " << name << "(";
-                        if (!EmitTypeAndName(f, atomic_ty, "atomic")) {
-                            return "";
-                        }
-                        f << ", ";
-                        if (!EmitTypeAndName(f, arg_ty, "compare")) {
-                            return "";
-                        }
-                        f << ", ";
-                        if (!EmitTypeAndName(f, arg_ty, "value")) {
-                            return "";
-                        }
-                        f << ") {";
-                    }
-
-                    buf.IncrementIndent();
-                    TINT_DEFER({
-                        buf.DecrementIndent();
-                        Line(&buf) << "}";
-                        Line(&buf);
-                    });
-
-                    {
-                        auto f = Line(&buf);
-                        if (!EmitTypeAndName(f, arg_ty, "old_value")) {
-                            return "";
-                        }
-                        f << " = compare;";
-                    }
-                    Line(&buf) << "bool exchanged = "
-                                  "atomic_compare_exchange_weak_explicit(atomic, "
-                                  "&old_value, value, memory_order_relaxed, "
-                                  "memory_order_relaxed);";
-                    Line(&buf) << "return {old_value, exchanged};";
-                    return name;
-                });
-
-            if (func.empty()) {
-                return false;
-            }
-            return call(func, false);
-        }
-
-        default:
-            break;
-    }
-
-    TINT_UNREACHABLE() << "unsupported atomic builtin: " << builtin->Fn();
-}
-
-bool ASTPrinter::EmitTextureCall(StringStream& out,
-                                 const sem::Call* call,
-                                 const sem::BuiltinFn* builtin) {
-    using Usage = core::ParameterUsage;
-
-    auto& signature = builtin->Signature();
-    auto* expr = call->Declaration();
-    auto& arguments = call->Arguments();
-
-    // Returns the argument with the given usage
-    auto arg = [&](Usage usage) {
-        int idx = signature.IndexOf(usage);
-        return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
-    };
-
-    auto* texture = arg(Usage::kTexture)->Declaration();
-    if (DAWN_UNLIKELY(!texture)) {
-        TINT_ICE() << "missing texture arg";
-    }
-
-    auto* texture_type = TypeOf(texture)->UnwrapRef()->As<core::type::Texture>();
-
-    // Helper to emit the texture expression, wrapped in parentheses if the
-    // expression includes an operator with lower precedence than the member
-    // accessor used for the function calls.
-    auto texture_expr = [&] {
-        bool paren_lhs = !texture->IsAnyOf<ast::AccessorExpression, ast::CallExpression,
-                                           ast::IdentifierExpression>();
-        if (paren_lhs) {
-            out << "(";
-        }
-        if (!EmitExpression(out, texture)) {
-            return false;
-        }
-        if (paren_lhs) {
-            out << ")";
-        }
-        return true;
-    };
-
-    // MSL requires that `lod` is a constant 0 for 1D textures.
-    bool level_is_constant_zero = texture_type->Dim() == core::type::TextureDimension::k1d;
-
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kTextureDimensions: {
-            std::vector<const char*> dims;
-            switch (texture_type->Dim()) {
-                case core::type::TextureDimension::kNone:
-                    diagnostics_.AddError(Source{}) << "texture dimension is kNone";
-                    return false;
-                case core::type::TextureDimension::k1d:
-                    dims = {"width"};
-                    break;
-                case core::type::TextureDimension::k2d:
-                case core::type::TextureDimension::k2dArray:
-                case core::type::TextureDimension::kCube:
-                case core::type::TextureDimension::kCubeArray:
-                    dims = {"width", "height"};
-                    break;
-                case core::type::TextureDimension::k3d:
-                    dims = {"width", "height", "depth"};
-                    break;
-            }
-
-            auto get_dim = [&](const char* name) {
-                if (!texture_expr()) {
-                    return false;
-                }
-                out << ".get_" << name << "(";
-                if (level_is_constant_zero) {
-                    out << "0";
-                } else {
-                    if (auto* level = arg(Usage::kLevel)) {
-                        if (!EmitExpression(out, level->Declaration())) {
-                            return false;
-                        }
-                    }
-                }
-                out << ")";
-                return true;
-            };
-
-            if (dims.size() == 1) {
-                get_dim(dims[0]);
-            } else {
-                EmitType(out, TypeOf(expr)->UnwrapRef());
-                out << "(";
-                for (size_t i = 0; i < dims.size(); i++) {
-                    if (i > 0) {
-                        out << ", ";
-                    }
-                    get_dim(dims[i]);
-                }
-                out << ")";
-            }
-            return true;
-        }
-        case wgsl::BuiltinFn::kTextureNumLayers: {
-            if (!texture_expr()) {
-                return false;
-            }
-            out << ".get_array_size()";
-            return true;
-        }
-        case wgsl::BuiltinFn::kTextureNumLevels: {
-            if (!texture_expr()) {
-                return false;
-            }
-            out << ".get_num_mip_levels()";
-            return true;
-        }
-        case wgsl::BuiltinFn::kTextureNumSamples: {
-            if (!texture_expr()) {
-                return false;
-            }
-            out << ".get_num_samples()";
-            return true;
-        }
-        default:
-            break;
-    }
-
-    if (!texture_expr()) {
-        return false;
-    }
-
-    bool lod_param_is_named = true;
-    bool is_gather_or_sample = false;
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kTextureSample:
-        case wgsl::BuiltinFn::kTextureSampleBias:
-        case wgsl::BuiltinFn::kTextureSampleLevel:
-        case wgsl::BuiltinFn::kTextureSampleGrad:
-            out << ".sample(";
-            is_gather_or_sample = true;
-            break;
-        case wgsl::BuiltinFn::kTextureSampleCompare:
-        case wgsl::BuiltinFn::kTextureSampleCompareLevel:
-            out << ".sample_compare(";
-            is_gather_or_sample = true;
-            break;
-        case wgsl::BuiltinFn::kTextureGather:
-            out << ".gather(";
-            is_gather_or_sample = true;
-            break;
-        case wgsl::BuiltinFn::kTextureGatherCompare:
-            out << ".gather_compare(";
-            is_gather_or_sample = true;
-            break;
-        case wgsl::BuiltinFn::kTextureLoad:
-            out << ".read(";
-            lod_param_is_named = false;
-            break;
-        case wgsl::BuiltinFn::kTextureStore:
-            out << ".write(";
-            break;
-        default:
-            TINT_UNREACHABLE() << "Unhandled texture builtin '" << builtin->str() << "'";
-    }
-
-    bool first_arg = true;
-    auto maybe_write_comma = [&] {
-        if (!first_arg) {
-            out << ", ";
-        }
-        first_arg = false;
-    };
-
-    for (auto usage : {Usage::kValue, Usage::kSampler, Usage::kCoords, Usage::kArrayIndex,
-                       Usage::kDepthRef, Usage::kSampleIndex}) {
-        if (auto* e = arg(usage)) {
-            maybe_write_comma();
-
-            // Cast the coordinates to unsigned integers if necessary.
-            bool inside_params = false;
-            if (usage == Usage::kCoords && e->Type()->UnwrapRef()->IsIntegerScalarOrVector()) {
-                inside_params = true;
-                switch (texture_type->Dim()) {
-                    case core::type::TextureDimension::k1d:
-                        out << "uint(";
-                        break;
-                    case core::type::TextureDimension::k2d:
-                    case core::type::TextureDimension::k2dArray:
-                        out << "uint2(";
-                        break;
-                    case core::type::TextureDimension::k3d:
-                        out << "uint3(";
-
-                        break;
-                    default:
-                        TINT_ICE() << "unhandled texture dimensionality";
-                }
-            } else if (usage == Usage::kArrayIndex &&
-                       e->Type()->UnwrapRef()->IsSignedIntegerScalar() && is_gather_or_sample) {
-                // Array index access for signed integers is zero lower bound clamped to emulate the
-                // behavior of other platforms. See crbug.com/202355.
-                out << "max(0, ";
-                inside_params = true;
-            }
-
-            if (!EmitExpression(out, e->Declaration())) {
-                return false;
-            }
-
-            if (inside_params) {
-                out << ")";
-            }
-        }
-    }
-
-    if (auto* bias = arg(Usage::kBias)) {
-        maybe_write_comma();
-        out << "bias(";
-        if (!EmitExpression(out, bias->Declaration())) {
-            return false;
-        }
-        out << ")";
-    }
-    if (auto* level = arg(Usage::kLevel)) {
-        maybe_write_comma();
-        if (lod_param_is_named) {
-            out << "level(";
-        }
-        if (level_is_constant_zero) {
-            out << "0";
-        } else {
-            if (!EmitExpression(out, level->Declaration())) {
-                return false;
-            }
-        }
-        if (lod_param_is_named) {
-            out << ")";
-        }
-    }
-    if (builtin->Fn() == wgsl::BuiltinFn::kTextureSampleCompareLevel) {
-        maybe_write_comma();
-        out << "level(0)";
-    }
-    if (auto* ddx = arg(Usage::kDdx)) {
-        auto dim = texture_type->Dim();
-        switch (dim) {
-            case core::type::TextureDimension::k2d:
-            case core::type::TextureDimension::k2dArray:
-                maybe_write_comma();
-                out << "gradient2d(";
-                break;
-            case core::type::TextureDimension::k3d:
-                maybe_write_comma();
-                out << "gradient3d(";
-                break;
-            case core::type::TextureDimension::kCube:
-            case core::type::TextureDimension::kCubeArray:
-                maybe_write_comma();
-                out << "gradientcube(";
-                break;
-            default: {
-                diagnostics_.AddError(Source{})
-                    << "MSL does not support gradients for " << dim << " textures";
-                return false;
-            }
-        }
-        if (!EmitExpression(out, ddx->Declaration())) {
-            return false;
-        }
-        out << ", ";
-        if (!EmitExpression(out, arg(Usage::kDdy)->Declaration())) {
-            return false;
-        }
-        out << ")";
-    }
-
-    bool has_offset = false;
-    if (auto* offset = arg(Usage::kOffset)) {
-        has_offset = true;
-        maybe_write_comma();
-        if (!EmitExpression(out, offset->Declaration())) {
-            return false;
-        }
-    }
-
-    if (auto* component = arg(Usage::kComponent)) {
-        maybe_write_comma();
-        if (!has_offset) {
-            // offset argument may need to be provided if we have a component.
-            switch (texture_type->Dim()) {
-                case core::type::TextureDimension::k2d:
-                case core::type::TextureDimension::k2dArray:
-                    out << "int2(0), ";
-                    break;
-                default:
-                    break;  // Other texture dimensions don't have an offset
-            }
-        }
-        auto c = component->ConstantValue()->ValueAs<AInt>();
-        switch (c.value) {
-            case 0:
-                out << "component::x";
-                break;
-            case 1:
-                out << "component::y";
-                break;
-            case 2:
-                out << "component::z";
-                break;
-            case 3:
-                out << "component::w";
-                break;
-            default:
-                TINT_ICE() << "invalid textureGather component: " << c;
-        }
-    }
-
-    out << ")";
-
-    // If this is a `textureStore()` for a read-write texture, add a fence to ensure that the
-    // written values are visible to subsequent reads from the same thread.
-    if (auto* storage = texture_type->As<core::type::StorageTexture>();
-        builtin->Fn() == wgsl::BuiltinFn::kTextureStore &&
-        storage->Access() == core::Access::kReadWrite) {
-        out << "; ";
-        texture_expr();
-        out << ".fence()";
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitDotCall(StringStream& out,
-                             const ast::CallExpression* expr,
-                             const sem::BuiltinFn* builtin) {
-    auto* vec_ty = builtin->Parameters()[0]->Type()->As<core::type::Vector>();
-    std::string fn = "dot";
-    if (vec_ty->Type()->IsIntegerScalar()) {
-        // MSL does not have a builtin for dot() with integer vector types.
-        // Generate the helper function if it hasn't been created already
-        fn = tint::GetOrAdd(int_dot_funcs_, vec_ty->Width(), [&]() -> std::string {
-            TextBuffer b;
-            TINT_DEFER(helpers_.Append(b));
-
-            auto fn_name = UniqueIdentifier("tint_dot" + std::to_string(vec_ty->Width()));
-            auto v = "vec<T," + std::to_string(vec_ty->Width()) + ">";
-
-            Line(&b) << "template<typename T>";
-            Line(&b) << "T " << fn_name << "(" << v << " a, " << v << " b) {";
-            {
-                auto l = Line(&b);
-                l << "  return ";
-                for (uint32_t i = 0; i < vec_ty->Width(); i++) {
-                    if (i > 0) {
-                        l << " + ";
-                    }
-                    l << "a[" << i << "]*b[" << i << "]";
-                }
-                l << ";";
-            }
-            Line(&b) << "}";
-            return fn_name;
-        });
-    }
-
-    out << fn << "(";
-    if (!EmitExpression(out, expr->args[0])) {
-        return false;
-    }
-    out << ", ";
-    if (!EmitExpression(out, expr->args[1])) {
-        return false;
-    }
-    out << ")";
-    return true;
-}
-
-bool ASTPrinter::EmitModfCall(StringStream& out,
-                              const ast::CallExpression* expr,
-                              const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(
-        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
-            auto* ty = builtin->Parameters()[0]->Type();
-            auto in = params[0];
-
-            std::string width;
-            if (auto* vec = ty->As<core::type::Vector>()) {
-                width = std::to_string(vec->Width());
-            }
-
-            // Emit the builtin return type unique to this overload. This does not
-            // exist in the AST, so it will not be generated in Generate().
-            if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
-                return false;
-            }
-
-            Line(b) << StructName(builtin->ReturnType()->As<core::type::Struct>()) << " result;";
-            Line(b) << "result.fract = modf(" << in << ", result.whole);";
-            Line(b) << "return result;";
-            return true;
-        });
-}
-
-bool ASTPrinter::EmitFrexpCall(StringStream& out,
-                               const ast::CallExpression* expr,
-                               const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(
-        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
-            auto* ty = builtin->Parameters()[0]->Type();
-            auto in = params[0];
-
-            std::string width;
-            if (auto* vec = ty->As<core::type::Vector>()) {
-                width = std::to_string(vec->Width());
-            }
-
-            // Emit the builtin return type unique to this overload. This does not
-            // exist in the AST, so it will not be generated in Generate().
-            if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
-                return false;
-            }
-
-            Line(b) << StructName(builtin->ReturnType()->As<core::type::Struct>()) << " result;";
-            Line(b) << "result.fract = frexp(" << in << ", result.exp);";
-            Line(b) << "return result;";
-            return true;
-        });
-}
-
-bool ASTPrinter::EmitDegreesCall(StringStream& out,
-                                 const ast::CallExpression* expr,
-                                 const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(out, expr, builtin,
-                             [&](TextBuffer* b, const std::vector<std::string>& params) {
-                                 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
-                                         << sem::kRadToDeg << ";";
-                                 return true;
-                             });
-}
-
-bool ASTPrinter::EmitRadiansCall(StringStream& out,
-                                 const ast::CallExpression* expr,
-                                 const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(out, expr, builtin,
-                             [&](TextBuffer* b, const std::vector<std::string>& params) {
-                                 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
-                                         << sem::kDegToRad << ";";
-                                 return true;
-                             });
-}
-
-std::string ASTPrinter::generate_builtin_name(const sem::BuiltinFn* builtin) {
-    std::string out = "";
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kAcos:
-        case wgsl::BuiltinFn::kAcosh:
-        case wgsl::BuiltinFn::kAll:
-        case wgsl::BuiltinFn::kAny:
-        case wgsl::BuiltinFn::kAsin:
-        case wgsl::BuiltinFn::kAsinh:
-        case wgsl::BuiltinFn::kAtanh:
-        case wgsl::BuiltinFn::kAtan:
-        case wgsl::BuiltinFn::kAtan2:
-        case wgsl::BuiltinFn::kCeil:
-        case wgsl::BuiltinFn::kCos:
-        case wgsl::BuiltinFn::kCosh:
-        case wgsl::BuiltinFn::kCross:
-        case wgsl::BuiltinFn::kDeterminant:
-        case wgsl::BuiltinFn::kDistance:
-        case wgsl::BuiltinFn::kDot:
-        case wgsl::BuiltinFn::kExp:
-        case wgsl::BuiltinFn::kExp2:
-        case wgsl::BuiltinFn::kFloor:
-        case wgsl::BuiltinFn::kFma:
-        case wgsl::BuiltinFn::kFract:
-        case wgsl::BuiltinFn::kFrexp:
-        case wgsl::BuiltinFn::kLength:
-        case wgsl::BuiltinFn::kLdexp:
-        case wgsl::BuiltinFn::kLog:
-        case wgsl::BuiltinFn::kLog2:
-        case wgsl::BuiltinFn::kMix:
-        case wgsl::BuiltinFn::kModf:
-        case wgsl::BuiltinFn::kNormalize:
-        case wgsl::BuiltinFn::kReflect:
-        case wgsl::BuiltinFn::kRefract:
-        case wgsl::BuiltinFn::kSaturate:
-        case wgsl::BuiltinFn::kSelect:
-        case wgsl::BuiltinFn::kSin:
-        case wgsl::BuiltinFn::kSinh:
-        case wgsl::BuiltinFn::kSqrt:
-        case wgsl::BuiltinFn::kStep:
-        case wgsl::BuiltinFn::kTan:
-        case wgsl::BuiltinFn::kTanh:
-        case wgsl::BuiltinFn::kTranspose:
-        case wgsl::BuiltinFn::kTrunc:
-        case wgsl::BuiltinFn::kSign:
-        case wgsl::BuiltinFn::kClamp:
-            out += builtin->str();
-            break;
-        case wgsl::BuiltinFn::kPow:
-            out += "powr";
-            break;
-        case wgsl::BuiltinFn::kAbs:
-            if (builtin->ReturnType()->IsFloatScalarOrVector()) {
-                out += "fabs";
-            } else {
-                out += "abs";
-            }
-            break;
-        case wgsl::BuiltinFn::kCountLeadingZeros:
-            out += "clz";
-            break;
-        case wgsl::BuiltinFn::kCountOneBits:
-            out += "popcount";
-            break;
-        case wgsl::BuiltinFn::kCountTrailingZeros:
-            out += "ctz";
-            break;
-        case wgsl::BuiltinFn::kDpdx:
-        case wgsl::BuiltinFn::kDpdxCoarse:
-        case wgsl::BuiltinFn::kDpdxFine:
-            out += "dfdx";
-            break;
-        case wgsl::BuiltinFn::kDpdy:
-        case wgsl::BuiltinFn::kDpdyCoarse:
-        case wgsl::BuiltinFn::kDpdyFine:
-            out += "dfdy";
-            break;
-        case wgsl::BuiltinFn::kExtractBits:
-            out += "extract_bits";
-            break;
-        case wgsl::BuiltinFn::kInsertBits:
-            out += "insert_bits";
-            break;
-        case wgsl::BuiltinFn::kFwidth:
-        case wgsl::BuiltinFn::kFwidthCoarse:
-        case wgsl::BuiltinFn::kFwidthFine:
-            out += "fwidth";
-            break;
-        case wgsl::BuiltinFn::kMax:
-            if (builtin->ReturnType()->IsFloatScalarOrVector()) {
-                out += "fmax";
-            } else {
-                out += "max";
-            }
-            break;
-        case wgsl::BuiltinFn::kMin:
-            if (builtin->ReturnType()->IsFloatScalarOrVector()) {
-                out += "fmin";
-            } else {
-                out += "min";
-            }
-            break;
-        case wgsl::BuiltinFn::kFaceForward:
-            out += "faceforward";
-            break;
-        case wgsl::BuiltinFn::kPack4X8Snorm:
-            out += "pack_float_to_snorm4x8";
-            break;
-        case wgsl::BuiltinFn::kPack4X8Unorm:
-            out += "pack_float_to_unorm4x8";
-            break;
-        case wgsl::BuiltinFn::kPack2X16Snorm:
-            out += "pack_float_to_snorm2x16";
-            break;
-        case wgsl::BuiltinFn::kPack2X16Unorm:
-            out += "pack_float_to_unorm2x16";
-            break;
-        case wgsl::BuiltinFn::kReverseBits:
-            out += "reverse_bits";
-            break;
-        case wgsl::BuiltinFn::kRound:
-            out += "rint";
-            break;
-        case wgsl::BuiltinFn::kSmoothstep:
-            out += "smoothstep";
-            break;
-        case wgsl::BuiltinFn::kInverseSqrt:
-            out += "rsqrt";
-            break;
-        case wgsl::BuiltinFn::kUnpack4X8Snorm:
-            out += "unpack_snorm4x8_to_float";
-            break;
-        case wgsl::BuiltinFn::kUnpack4X8Unorm:
-            out += "unpack_unorm4x8_to_float";
-            break;
-        case wgsl::BuiltinFn::kUnpack2X16Snorm:
-            out += "unpack_snorm2x16_to_float";
-            break;
-        case wgsl::BuiltinFn::kUnpack2X16Unorm:
-            out += "unpack_unorm2x16_to_float";
-            break;
-        case wgsl::BuiltinFn::kArrayLength:
-            diagnostics_.AddError(Source{})
-                << "Unable to translate builtin: " << builtin->Fn()
-                << "\nDid you forget to pass array_length_from_uniform generator options?";
-            return "";
-        default:
-            diagnostics_.AddError(Source{}) << "Unknown import method: " << builtin->Fn();
-            return "";
-    }
-    return out;
-}
-
-bool ASTPrinter::EmitCase(const ast::CaseStatement* stmt) {
-    auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
-    for (auto* selector : sem->Selectors()) {
-        auto out = Line();
-
-        if (selector->IsDefault()) {
-            out << "default";
-        } else {
-            out << "case ";
-            if (!EmitConstant(out, selector->Value())) {
-                return false;
-            }
-        }
-        out << ":";
-        if (selector == sem->Selectors().back()) {
-            out << " {";
-        }
-    }
-
-    {
-        ScopedIndent si(this);
-
-        for (auto* s : stmt->body->statements) {
-            if (!EmitStatement(s)) {
-                return false;
-            }
-        }
-
-        if (!last_is_break(stmt->body)) {
-            Line() << "break;";
-        }
-    }
-
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitContinue(const ast::ContinueStatement*) {
-    if (!emit_continuing_ || !emit_continuing_()) {
-        return false;
-    }
-
-    Line() << "continue;";
-    return true;
-}
-
-bool ASTPrinter::EmitZeroValue(StringStream& out, const core::type::Type* type) {
-    return Switch(
-        type,
-        [&](const core::type::Bool*) {
-            out << "false";
-            return true;
-        },
-        [&](const core::type::F16*) {
-            out << "0.0h";
-            return true;
-        },
-        [&](const core::type::F32*) {
-            out << "0.0f";
-            return true;
-        },
-        [&](const core::type::I32*) {
-            out << "0";
-            return true;
-        },
-        [&](const core::type::U32*) {
-            out << "0u";
-            return true;
-        },
-        [&](const core::type::Vector* vec) {  //
-            return EmitZeroValue(out, vec->Type());
-        },
-        [&](const core::type::Matrix* mat) {
-            if (!EmitType(out, mat)) {
-                return false;
-            }
-            ScopedParen sp(out);
-            return EmitZeroValue(out, mat->Type());
-        },
-        [&](const core::type::Array*) {
-            out << "{}";
-            return true;
-        },
-        [&](const core::type::Struct*) {
-            out << "{}";
-            return true;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitConstant(StringStream& out, const core::constant::Value* constant) {
-    return Switch(
-        constant->Type(),  //
-        [&](const core::type::Bool*) {
-            out << (constant->ValueAs<AInt>() ? "true" : "false");
-            return true;
-        },
-        [&](const core::type::F32*) {
-            PrintF32(out, constant->ValueAs<f32>());
-            return true;
-        },
-        [&](const core::type::F16*) {
-            PrintF16(out, constant->ValueAs<f16>());
-            return true;
-        },
-        [&](const core::type::I32*) {
-            PrintI32(out, constant->ValueAs<i32>());
-            return true;
-        },
-        [&](const core::type::U32*) {
-            out << constant->ValueAs<AInt>() << "u";
-            return true;
-        },
-        [&](const core::type::Vector* v) {
-            if (!EmitType(out, v)) {
-                return false;
-            }
-
-            ScopedParen sp(out);
-
-            if (auto* splat = constant->As<core::constant::Splat>()) {
-                if (!EmitConstant(out, splat->el)) {
-                    return false;
-                }
-                return true;
-            }
-
-            for (size_t i = 0; i < v->Width(); i++) {
-                if (i > 0) {
-                    out << ", ";
-                }
-                if (!EmitConstant(out, constant->Index(i))) {
-                    return false;
-                }
-            }
-            return true;
-        },
-        [&](const core::type::Matrix* m) {
-            if (!EmitType(out, m)) {
-                return false;
-            }
-
-            ScopedParen sp(out);
-
-            for (size_t i = 0; i < m->Columns(); i++) {
-                if (i > 0) {
-                    out << ", ";
-                }
-                if (!EmitConstant(out, constant->Index(i))) {
-                    return false;
-                }
-            }
-            return true;
-        },
-        [&](const core::type::Array* a) {
-            if (!EmitType(out, a)) {
-                return false;
-            }
-
-            out << "{";
-            TINT_DEFER(out << "}");
-
-            if (constant->AllZero()) {
-                return true;
-            }
-
-            auto count = a->ConstantCount();
-            if (!count) {
-                diagnostics_.AddError(Source{}) << core::type::Array::kErrExpectedConstantCount;
-                return false;
-            }
-
-            for (size_t i = 0; i < count; i++) {
-                if (i > 0) {
-                    out << ", ";
-                }
-                if (!EmitConstant(out, constant->Index(i))) {
-                    return false;
-                }
-            }
-
-            return true;
-        },
-        [&](const core::type::Struct* s) {
-            if (!EmitStructType(&helpers_, s)) {
-                return false;
-            }
-
-            out << StructName(s) << "{";
-            TINT_DEFER(out << "}");
-
-            if (constant->AllZero()) {
-                return true;
-            }
-
-            auto members = s->Members();
-            for (size_t i = 0; i < members.Length(); i++) {
-                if (i > 0) {
-                    out << ", ";
-                }
-                out << "." << members[i]->Name().Name() << "=";
-                if (!EmitConstant(out, constant->Index(i))) {
-                    return false;
-                }
-            }
-
-            return true;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitLiteral(StringStream& out, const ast::LiteralExpression* lit) {
-    return Switch(
-        lit,
-        [&](const ast::BoolLiteralExpression* l) {
-            out << (l->value ? "true" : "false");
-            return true;
-        },
-        [&](const ast::FloatLiteralExpression* l) {
-            if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
-                PrintF16(out, static_cast<float>(l->value));
-            } else {
-                PrintF32(out, static_cast<float>(l->value));
-            }
-            return true;
-        },
-        [&](const ast::IntLiteralExpression* i) {
-            switch (i->suffix) {
-                case ast::IntLiteralExpression::Suffix::kNone:
-                case ast::IntLiteralExpression::Suffix::kI: {
-                    PrintI32(out, static_cast<int32_t>(i->value));
-                    return true;
-                }
-                case ast::IntLiteralExpression::Suffix::kU: {
-                    out << i->value << "u";
-                    return true;
-                }
-            }
-            diagnostics_.AddError(Source{}) << "unknown integer literal suffix type";
-            return false;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitExpression(StringStream& out, const ast::Expression* expr) {
-    if (auto* sem = builder_.Sem().GetVal(expr)) {
-        if (auto* constant = sem->ConstantValue()) {
-            return EmitConstant(out, constant);
-        }
-    }
-    return Switch(
-        expr,  //
-        [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(out, a); },
-        [&](const ast::BinaryExpression* b) { return EmitBinary(out, b); },
-        [&](const ast::CallExpression* c) { return EmitCall(out, c); },
-        [&](const ast::IdentifierExpression* i) { return EmitIdentifier(out, i); },
-        [&](const ast::LiteralExpression* l) { return EmitLiteral(out, l); },
-        [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(out, m); },
-        [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(out, u); },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-void ASTPrinter::EmitStage(StringStream& out, ast::PipelineStage stage) {
-    switch (stage) {
-        case ast::PipelineStage::kFragment:
-            out << "fragment";
-            break;
-        case ast::PipelineStage::kVertex:
-            out << "vertex";
-            break;
-        case ast::PipelineStage::kCompute:
-            out << "kernel";
-            break;
-        case ast::PipelineStage::kNone:
-            break;
-    }
-    return;
-}
-
-bool ASTPrinter::EmitFunction(const ast::Function* func) {
-    if (func->body == nullptr) {
-        // An internal function. Do not emit.
-        return true;
-    }
-
-    auto* func_sem = builder_.Sem().Get(func);
-
-    {
-        auto out = Line();
-        if (!EmitType(out, func_sem->ReturnType())) {
-            return false;
-        }
-        out << " " << func->name->symbol.Name() << "(";
-
-        bool first = true;
-        for (auto* v : func->params) {
-            if (!first) {
-                out << ", ";
-            }
-            first = false;
-
-            auto* type = builder_.Sem().Get(v)->Type();
-
-            if (!EmitType(out, type)) {
-                return false;
-            }
-            if (type->Is<core::type::Pointer>()) {
-                out << " const";
-            }
-            out << " " << v->name->symbol.Name();
-        }
-
-        out << ") {";
-    }
-
-    if (!EmitStatementsWithIndent(func->body->statements)) {
-        return false;
-    }
-
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitEntryPointFunction(const ast::Function* func) {
-    auto* func_sem = builder_.Sem().Get(func);
-
-    auto func_name = func->name->symbol.Name();
-    workgroup_allocations_.insert({func_name, {}});
-
-    // Returns the binding index of a variable, requiring that the group
-    // attribute have a value of zero.
-    const uint32_t kInvalidBindingIndex = std::numeric_limits<uint32_t>::max();
-    auto get_binding_index = [&](const ast::Parameter* param) -> uint32_t {
-        if (DAWN_UNLIKELY(!param->HasBindingPoint())) {
-            TINT_ICE() << "missing binding attributes for entry point parameter";
-        }
-        auto* param_sem = builder_.Sem().Get(param);
-        auto bp = param_sem->Attributes().binding_point;
-        if (DAWN_UNLIKELY(bp->group != 0)) {
-            TINT_ICE() << "encountered non-zero resource group index (use BindingRemapper to fix)";
-        }
-        return bp->binding;
-    };
-
-    {
-        auto out = Line();
-
-        EmitStage(out, func->PipelineStage());
-        out << " ";
-        if (!EmitTypeAndName(out, func_sem->ReturnType(), func_name)) {
-            return false;
-        }
-        out << "(";
-
-        // Emit entry point parameters.
-        bool first = true;
-        for (auto* param : func->params) {
-            if (!first) {
-                out << ", ";
-            }
-            first = false;
-
-            auto* type = builder_.Sem().Get(param)->Type()->UnwrapRef();
-
-            if (!EmitType(out, type)) {
-                return false;
-            }
-            out << " " << param->name->symbol.Name();
-
-            bool ok = Switch(
-                type,  //
-                [&](const core::type::Struct*) {
-                    out << " [[stage_in]]";
-                    return true;
-                },
-                [&](const core::type::Texture*) {
-                    uint32_t binding = get_binding_index(param);
-                    if (binding == kInvalidBindingIndex) {
-                        return false;
-                    }
-                    out << " [[texture(" << binding << ")]]";
-                    return true;
-                },
-                [&](const core::type::Sampler*) {
-                    uint32_t binding = get_binding_index(param);
-                    if (binding == kInvalidBindingIndex) {
-                        return false;
-                    }
-                    out << " [[sampler(" << binding << ")]]";
-                    return true;
-                },
-                [&](const core::type::Pointer* ptr) {
-                    switch (ptr->AddressSpace()) {
-                        case core::AddressSpace::kWorkgroup: {
-                            auto& allocations = workgroup_allocations_[func_name];
-                            out << " [[threadgroup(" << allocations.size() << ")]]";
-                            allocations.push_back(ptr->StoreType()->Size());
-                            return true;
-                        }
-
-                        case core::AddressSpace::kStorage:
-                        case core::AddressSpace::kUniform: {
-                            uint32_t binding = get_binding_index(param);
-                            if (binding == kInvalidBindingIndex) {
-                                return false;
-                            }
-                            out << " [[buffer(" << binding << ")]]";
-                            return true;
-                        }
-
-                        default:
-                            break;
-                    }
-                    TINT_ICE() << "invalid pointer address space for entry point parameter";
-                },
-                [&](Default) {
-                    auto& attrs = param->attributes;
-                    bool builtin_found = false;
-                    for (auto* attr : attrs) {
-                        if (attr->Is<QuadSwap::ThreadIndexInQuadgroup>()) {
-                            out << " [[thread_index_in_quadgroup]]";
-                            builtin_found = true;
-                            continue;
-                        }
-
-                        auto* builtin_attr = attr->As<ast::BuiltinAttribute>();
-                        if (!builtin_attr) {
-                            continue;
-                        }
-
-                        builtin_found = true;
-
-                        auto name = BuiltinToAttribute(builtin_attr->builtin);
-                        if (name.empty()) {
-                            diagnostics_.AddError(Source{}) << "unknown builtin";
-                            return false;
-                        }
-
-                        out << " [[" << name << "]]";
-                    }
-                    if (DAWN_UNLIKELY(!builtin_found)) {
-                        TINT_ICE() << "Unsupported entry point parameter";
-                    }
-                    return true;
-                });
-            if (!ok) {
-                return false;
-            }
-        }
-        out << ") {";
-    }
-
-    {
-        ScopedIndent si(this);
-
-        if (!EmitStatements(func->body->statements)) {
-            return false;
-        }
-
-        if (!Is<ast::ReturnStatement>(func->body->Last())) {
-            ast::ReturnStatement ret(GenerationID{}, ast::NodeID{}, Source{});
-            if (!EmitStatement(&ret)) {
-                return false;
-            }
-        }
-    }
-
-    Line() << "}";
-    return true;
-}
-
-bool ASTPrinter::EmitIdentifier(StringStream& out, const ast::IdentifierExpression* expr) {
-    out << expr->identifier->symbol.Name();
-    return true;
-}
-
-bool ASTPrinter::EmitLoop(const ast::LoopStatement* stmt) {
-    auto emit_continuing = [this, stmt] {
-        if (stmt->continuing && !stmt->continuing->Empty()) {
-            if (!EmitBlock(stmt->continuing)) {
-                return false;
-            }
-        }
-        return true;
-    };
-
-    TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-    Line() << "while(true) {";
-    {
-        ScopedIndent si(this);
-        IsolateUBIfNeeded();
-        if (!EmitStatements(stmt->body->statements)) {
-            return false;
-        }
-        if (!emit_continuing_()) {
-            return false;
-        }
-    }
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitForLoop(const ast::ForLoopStatement* stmt) {
-    TextBuffer init_buf;
-    if (auto* init = stmt->initializer) {
-        TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
-        if (!EmitStatement(init)) {
-            return false;
-        }
-    }
-
-    TextBuffer cond_pre;
-    StringStream cond_buf;
-    if (auto* cond = stmt->condition) {
-        TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
-        if (!EmitExpression(cond_buf, cond)) {
-            return false;
-        }
-    }
-
-    TextBuffer cont_buf;
-    if (auto* cont = stmt->continuing) {
-        TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
-        if (!EmitStatement(cont)) {
-            return false;
-        }
-    }
-
-    // If the for-loop has a multi-statement conditional and / or continuing,
-    // then we cannot emit this as a regular for-loop in MSL. Instead we need to
-    // generate a `while(true)` loop.
-    bool emit_as_loop = cond_pre.lines.size() > 0 || cont_buf.lines.size() > 1;
-
-    // If the for-loop has multi-statement initializer, or is going to be
-    // emitted as a `while(true)` loop, then declare the initializer
-    // statement(s) before the loop in a new block.
-    bool nest_in_block = init_buf.lines.size() > 1 || (stmt->initializer && emit_as_loop);
-    if (nest_in_block) {
-        Line() << "{";
-        IncrementIndent();
-        current_buffer_->Append(init_buf);
-        init_buf.lines.clear();  // Don't emit the initializer again in the 'for'
-    }
-    TINT_DEFER({
-        if (nest_in_block) {
-            DecrementIndent();
-            Line() << "}";
-        }
-    });
-
-    if (emit_as_loop) {
-        auto emit_continuing = [&] {
-            current_buffer_->Append(cont_buf);
-            return true;
-        };
-
-        TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-        Line() << "while(true) {";
-        IncrementIndent();
-        TINT_DEFER({
-            DecrementIndent();
-            Line() << "}";
-        });
-        IsolateUBIfNeeded();
-
-        if (stmt->condition) {
-            current_buffer_->Append(cond_pre);
-            Line() << "if (!(" << cond_buf.str() << ")) { break; }";
-        }
-
-        if (!EmitStatements(stmt->body->statements)) {
-            return false;
-        }
-
-        if (!emit_continuing_()) {
-            return false;
-        }
-    } else {
-        // For-loop can be generated.
-        {
-            auto out = Line();
-            out << "for";
-            {
-                ScopedParen sp(out);
-
-                if (!init_buf.lines.empty()) {
-                    out << init_buf.lines[0].content << " ";
-                } else {
-                    out << "; ";
-                }
-
-                out << cond_buf.str() << "; ";
-
-                if (!cont_buf.lines.empty()) {
-                    out << tint::TrimSuffix(cont_buf.lines[0].content, ";");
-                }
-            }
-            out << " {";
-        }
-        {
-            IncrementIndent();
-            IsolateUBIfNeeded();
-            DecrementIndent();
-            auto emit_continuing = [] { return true; };
-            TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-            if (!EmitStatementsWithIndent(stmt->body->statements)) {
-                return false;
-            }
-        }
-        Line() << "}";
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitWhile(const ast::WhileStatement* stmt) {
-    TextBuffer cond_pre;
-    StringStream cond_buf;
-
-    {
-        auto* cond = stmt->condition;
-        TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
-        if (!EmitExpression(cond_buf, cond)) {
-            return false;
-        }
-    }
-
-    auto emit_continuing = [&] { return true; };
-    TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-
-    // If the while has a multi-statement conditional, then we cannot emit this
-    // as a regular while in MSL. Instead we need to generate a `while(true)` loop.
-    bool emit_as_loop = cond_pre.lines.size() > 0;
-    if (emit_as_loop) {
-        Line() << "while(true) {";
-        IncrementIndent();
-        IsolateUBIfNeeded();
-        TINT_DEFER({
-            DecrementIndent();
-            Line() << "}";
-        });
-
-        current_buffer_->Append(cond_pre);
-        Line() << "if (!(" << cond_buf.str() << ")) { break; }";
-        if (!EmitStatements(stmt->body->statements)) {
-            return false;
-        }
-    } else {
-        // While can be generated.
-        Line() << "while(" << cond_buf.str() << ") {";
-        IncrementIndent();
-        IsolateUBIfNeeded();
-        DecrementIndent();
-        if (!EmitStatementsWithIndent(stmt->body->statements)) {
-            return false;
-        }
-        Line() << "}";
-    }
-    return true;
-}
-
-bool ASTPrinter::EmitDiscard(const ast::DiscardStatement*) {
-    // TODO(dsinclair): Verify this is correct when the discard semantics are
-    // defined for WGSL (https://github.com/gpuweb/gpuweb/issues/361)
-    Line() << "discard_fragment();";
-    return true;
-}
-
-bool ASTPrinter::EmitIf(const ast::IfStatement* stmt) {
-    {
-        auto out = Line();
-        out << "if (";
-        if (!EmitExpression(out, stmt->condition)) {
-            return false;
-        }
-        out << ") {";
-    }
-
-    if (!EmitStatementsWithIndent(stmt->body->statements)) {
-        return false;
-    }
-
-    if (stmt->else_statement) {
-        Line() << "} else {";
-        if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
-            if (!EmitStatementsWithIndent(block->statements)) {
-                return false;
-            }
-        } else {
-            if (!EmitStatementsWithIndent(Vector{stmt->else_statement})) {
-                return false;
-            }
-        }
-    }
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitMemberAccessor(StringStream& out, const ast::MemberAccessorExpression* expr) {
-    auto write_lhs = [&] {
-        bool paren_lhs = !expr->object->IsAnyOf<ast::AccessorExpression, ast::CallExpression,
-                                                ast::IdentifierExpression>();
-        if (paren_lhs) {
-            out << "(";
-        }
-        if (!EmitExpression(out, expr->object)) {
-            return false;
-        }
-        if (paren_lhs) {
-            out << ")";
-        }
-        return true;
-    };
-
-    auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
-
-    return Switch(
-        sem,
-        [&](const sem::Swizzle* swizzle) {
-            // Metal did not add support for swizzle syntax with packed vector types until
-            // Metal 2.1, so we need to use the index operator for single-element selection instead.
-            // For multi-component swizzles, the PackedVec3 transform will have inserted casts to
-            // the non-packed types, so we can safely use swizzle syntax here.
-            if (swizzle->Indices().Length() == 1) {
-                if (!write_lhs()) {
-                    return false;
-                }
-                out << "[" << swizzle->Indices()[0] << "]";
-            } else {
-                if (!write_lhs()) {
-                    return false;
-                }
-                out << "." << expr->member->symbol.Name();
-            }
-            return true;
-        },
-        [&](const sem::StructMemberAccess* member_access) {
-            if (!write_lhs()) {
-                return false;
-            }
-            out << "." << member_access->Member()->Name().Name();
-            return true;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitReturn(const ast::ReturnStatement* stmt) {
-    auto out = Line();
-    out << "return";
-    if (stmt->value) {
-        out << " ";
-        if (!EmitExpression(out, stmt->value)) {
-            return false;
-        }
-    }
-    out << ";";
-    return true;
-}
-
-bool ASTPrinter::EmitBlock(const ast::BlockStatement* stmt) {
-    Line() << "{";
-
-    if (!EmitStatementsWithIndent(stmt->statements)) {
-        return false;
-    }
-
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitStatement(const ast::Statement* stmt) {
-    return Switch(
-        stmt,
-        [&](const ast::AssignmentStatement* a) {  //
-            return EmitAssign(a);
-        },
-        [&](const ast::BlockStatement* b) {  //
-            return EmitBlock(b);
-        },
-        [&](const ast::BreakStatement* b) {  //
-            return EmitBreak(b);
-        },
-        [&](const ast::BreakIfStatement* b) {  //
-            return EmitBreakIf(b);
-        },
-        [&](const ast::CallStatement* c) {  //
-            auto out = Line();
-            if (!EmitCall(out, c->expr)) {  //
-                return false;
-            }
-            out << ";";
-            return true;
-        },
-        [&](const ast::ContinueStatement* c) {  //
-            return EmitContinue(c);
-        },
-        [&](const ast::DiscardStatement* d) {  //
-            return EmitDiscard(d);
-        },
-        [&](const ast::IfStatement* i) {  //
-            return EmitIf(i);
-        },
-        [&](const ast::LoopStatement* l) {  //
-            return EmitLoop(l);
-        },
-        [&](const ast::ForLoopStatement* l) {  //
-            return EmitForLoop(l);
-        },
-        [&](const ast::WhileStatement* l) {  //
-            return EmitWhile(l);
-        },
-        [&](const ast::ReturnStatement* r) {  //
-            return EmitReturn(r);
-        },
-        [&](const ast::SwitchStatement* s) {  //
-            return EmitSwitch(s);
-        },
-        [&](const ast::VariableDeclStatement* v) {  //
-            return Switch(
-                v->variable,  //
-                [&](const ast::Var* var) { return EmitVar(var); },
-                [&](const ast::Let* let) { return EmitLet(let); },
-                [&](const ast::Const*) {
-                    return true;  // Constants are embedded at their use
-                },                //
-                TINT_ICE_ON_NO_MATCH);
-        },
-        [&](const ast::ConstAssert*) {
-            return true;  // Not emitted
-        },                //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitStatements(VectorRef<const ast::Statement*> stmts) {
-    for (auto* s : stmts) {
-        if (!EmitStatement(s)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool ASTPrinter::EmitStatementsWithIndent(VectorRef<const ast::Statement*> stmts) {
-    ScopedIndent si(this);
-    return EmitStatements(stmts);
-}
-
-bool ASTPrinter::EmitSwitch(const ast::SwitchStatement* stmt) {
-    {
-        auto out = Line();
-        out << "switch(";
-        if (!EmitExpression(out, stmt->condition)) {
-            return false;
-        }
-        out << ") {";
-    }
-
-    {
-        ScopedIndent si(this);
-        for (auto* s : stmt->body) {
-            if (!EmitCase(s)) {
-                return false;
-            }
-        }
-    }
-
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitType(StringStream& out, const core::type::Type* type) {
-    return Switch(
-        type,
-        [&](const core::type::Atomic* atomic) {
-            if (atomic->Type()->Is<core::type::I32>()) {
-                out << "atomic_int";
-                return true;
-            }
-            if (DAWN_LIKELY(atomic->Type()->Is<core::type::U32>())) {
-                out << "atomic_uint";
-                return true;
-            }
-            TINT_ICE() << "unhandled atomic type " << atomic->Type()->FriendlyName();
-        },
-        [&](const core::type::Array* arr) {
-            out << ArrayType() << "<";
-            if (!EmitType(out, arr->ElemType())) {
-                return false;
-            }
-            out << ", ";
-            if (arr->Count()->Is<core::type::RuntimeArrayCount>()) {
-                out << "1";
-            } else {
-                auto count = arr->ConstantCount();
-                if (!count) {
-                    diagnostics_.AddError(Source{}) << core::type::Array::kErrExpectedConstantCount;
-                    return false;
-                }
-
-                out << count.value();
-            }
-            out << ">";
-            return true;
-        },
-        [&](const core::type::Bool*) {
-            out << "bool";
-            return true;
-        },
-        [&](const core::type::F16*) {
-            out << "half";
-            return true;
-        },
-        [&](const core::type::F32*) {
-            out << "float";
-            return true;
-        },
-        [&](const core::type::I32*) {
-            out << "int";
-            return true;
-        },
-        [&](const core::type::Matrix* mat) {
-            if (!EmitType(out, mat->Type())) {
-                return false;
-            }
-            out << mat->Columns() << "x" << mat->Rows();
-            return true;
-        },
-        [&](const core::type::Pointer* ptr) {
-            if (ptr->Access() == core::Access::kRead) {
-                out << "const ";
-            }
-            if (!EmitAddressSpace(out, ptr->AddressSpace())) {
-                return false;
-            }
-            out << " ";
-            if (!EmitType(out, ptr->StoreType())) {
-                return false;
-            }
-            out << "*";
-            return true;
-        },
-        [&](const core::type::Sampler*) {
-            out << "sampler";
-            return true;
-        },
-        [&](const core::type::Struct* str) {
-            // Make sure the struct type gets emitted. There are some cases where the types are
-            // defined internal (like modf) which can end up in structures. The usage may be
-            // removed by phonies, but the declaration still needs to exist.
-            if (!EmitStructType(&helpers_, str)) {
-                return false;
-            }
-
-            // The struct type emits as just the name. The declaration would be
-            // emitted as part of emitting the declared types.
-            out << StructName(str);
-            return true;
-        },
-        [&](const core::type::Texture* tex) {
-            if (DAWN_UNLIKELY(tex->Is<core::type::ExternalTexture>())) {
-                TINT_ICE() << "Multiplanar external texture transform was not run.";
-            }
-
-            if (tex->IsAnyOf<core::type::DepthTexture, core::type::DepthMultisampledTexture>()) {
-                out << "depth";
-            } else {
-                out << "texture";
-            }
-
-            switch (tex->Dim()) {
-                case core::type::TextureDimension::k1d:
-                    out << "1d";
-                    break;
-                case core::type::TextureDimension::k2d:
-                    out << "2d";
-                    break;
-                case core::type::TextureDimension::k2dArray:
-                    out << "2d_array";
-                    break;
-                case core::type::TextureDimension::k3d:
-                    out << "3d";
-                    break;
-                case core::type::TextureDimension::kCube:
-                    out << "cube";
-                    break;
-                case core::type::TextureDimension::kCubeArray:
-                    out << "cube_array";
-                    break;
-                default:
-                    diagnostics_.AddError(Source{}) << "Invalid texture dimensions";
-                    return false;
-            }
-            if (tex->IsAnyOf<core::type::MultisampledTexture,
-                             core::type::DepthMultisampledTexture>()) {
-                out << "_ms";
-            }
-            out << "<";
-            TINT_DEFER(out << ">");
-
-            return Switch(
-                tex,
-                [&](const core::type::DepthTexture*) {
-                    out << "float, access::sample";
-                    return true;
-                },
-                [&](const core::type::DepthMultisampledTexture*) {
-                    out << "float, access::read";
-                    return true;
-                },
-                [&](const core::type::StorageTexture* storage) {
-                    if (!EmitType(out, storage->Type())) {
-                        return false;
-                    }
-
-                    std::string access_str;
-                    if (storage->Access() == core::Access::kRead) {
-                        out << ", access::read";
-                    } else if (storage->Access() == core::Access::kReadWrite) {
-                        out << ", access::read_write";
-                    } else if (storage->Access() == core::Access::kWrite) {
-                        out << ", access::write";
-                    } else {
-                        diagnostics_.AddError(Source{})
-                            << "Invalid access control for storage texture";
-                        return false;
-                    }
-                    return true;
-                },
-                [&](const core::type::MultisampledTexture* ms) {
-                    if (!EmitType(out, ms->Type())) {
-                        return false;
-                    }
-                    out << ", access::read";
-                    return true;
-                },
-                [&](const core::type::SampledTexture* sampled) {
-                    if (!EmitType(out, sampled->Type())) {
-                        return false;
-                    }
-                    out << ", access::sample";
-                    return true;
-                },  //
-                TINT_ICE_ON_NO_MATCH);
-        },
-        [&](const core::type::U32*) {
-            out << "uint";
-            return true;
-        },
-        [&](const core::type::Vector* vec) {
-            if (vec->Packed()) {
-                out << "packed_";
-            }
-            if (!EmitType(out, vec->Type())) {
-                return false;
-            }
-            out << vec->Width();
-            return true;
-        },
-        [&](const core::type::Void*) {
-            out << "void";
-            return true;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitTypeAndName(StringStream& out,
-                                 const core::type::Type* type,
-                                 const std::string& name) {
-    if (!EmitType(out, type)) {
-        return false;
-    }
-    out << " " << name;
-    return true;
-}
-
-bool ASTPrinter::EmitAddressSpace(StringStream& out, core::AddressSpace sc) {
-    switch (sc) {
-        case core::AddressSpace::kFunction:
-        case core::AddressSpace::kPrivate:
-        case core::AddressSpace::kHandle:
-            out << "thread";
-            return true;
-        case core::AddressSpace::kWorkgroup:
-            out << "threadgroup";
-            return true;
-        case core::AddressSpace::kStorage:
-            out << "device";
-            return true;
-        case core::AddressSpace::kUniform:
-            out << "constant";
-            return true;
-        default:
-            break;
-    }
-    TINT_ICE() << "unhandled address space: " << sc;
-}
-
-bool ASTPrinter::EmitStructType(TextBuffer* b, const core::type::Struct* str) {
-    auto it = emitted_structs_.emplace(str);
-    if (!it.second) {
-        return true;
-    }
-
-    Line(b) << "struct " << StructName(str) << " {";
-
-    bool is_host_shareable = str->IsHostShareable();
-
-    // Emits a `/* 0xnnnn */` byte offset comment for a struct member.
-    auto add_byte_offset_comment = [&](StringStream& out, uint32_t offset) {
-        std::ios_base::fmtflags saved_flag_state(out.flags());
-        out << "/* 0x" << std::hex << std::setfill('0') << std::setw(4) << offset << " */ ";
-        out.flags(saved_flag_state);
-    };
-
-    auto add_padding = [&](uint32_t size, uint32_t msl_offset) {
-        std::string name;
-        do {
-            name = UniqueIdentifier("tint_pad");
-        } while (str->FindMember(builder_.Symbols().Get(name)));
-
-        auto out = Line(b);
-        add_byte_offset_comment(out, msl_offset);
-        out << ArrayType() << "<int8_t, " << size << "> " << name << ";";
-    };
-
-    b->IncrementIndent();
-
-    uint32_t msl_offset = 0;
-    for (auto* mem : str->Members()) {
-        auto out = Line(b);
-        auto mem_name = mem->Name().Name();
-        auto wgsl_offset = mem->Offset();
-
-        if (is_host_shareable) {
-            if (DAWN_UNLIKELY(wgsl_offset < msl_offset)) {
-                // Unimplementable layout
-                TINT_ICE() << "Structure member WGSL offset (" << wgsl_offset
-                           << ") is behind MSL offset (" << msl_offset << ")";
-            }
-
-            // Generate padding if required
-            if (auto padding = wgsl_offset - msl_offset) {
-                add_padding(padding, msl_offset);
-                msl_offset += padding;
-            }
-
-            add_byte_offset_comment(out, msl_offset);
-        }
-
-        auto* ty = mem->Type();
-
-        // Emit attributes
-        auto& attributes = mem->Attributes();
-        std::string builtin_value_name;
-        if (auto builtin = attributes.builtin) {
-            builtin_value_name = BuiltinToAttribute(builtin.value());
-            if (builtin_value_name.empty()) {
-                diagnostics_.AddError(Source{}) << "unknown builtin";
-                return false;
-            }
-
-            // Emit `[[clip_distance]]` as a C-style f32 array
-            if (builtin == core::BuiltinValue::kClipDistances) {
-                const auto* arrayType = mem->Type()->As<core::type::Array>();
-                if (DAWN_UNLIKELY(arrayType == nullptr ||
-                                  !arrayType->ConstantCount().has_value())) {
-                    TINT_ICE() << "The type of `clip_distances` is not a sized array";
-                } else {
-                    out << "float " << mem_name << " [[" << builtin_value_name << "]] ["
-                        << *arrayType->ConstantCount() << "];";
-                }
-                continue;
-            }
-        }
-
-        if (!EmitType(out, mem->Type())) {
-            return false;
-        }
-
-        out << " " << mem_name;
-
-        if (!builtin_value_name.empty()) {
-            out << " [[" << builtin_value_name << "]]";
-        }
-
-        if (auto location = attributes.location) {
-            auto& pipeline_stage_uses = str->PipelineStageUses();
-            if (DAWN_UNLIKELY(pipeline_stage_uses.Count() != 1)) {
-                TINT_ICE() << "invalid entry point IO struct uses for " << str->Name().NameView();
-            }
-
-            if (pipeline_stage_uses.Contains(core::type::PipelineStageUsage::kVertexInput)) {
-                out << " [[attribute(" + std::to_string(location.value()) + ")]]";
-            } else if (pipeline_stage_uses.Contains(
-                           core::type::PipelineStageUsage::kVertexOutput)) {
-                out << " [[user(locn" + std::to_string(location.value()) + ")]]";
-            } else if (pipeline_stage_uses.Contains(
-                           core::type::PipelineStageUsage::kFragmentInput)) {
-                out << " [[user(locn" + std::to_string(location.value()) + ")]]";
-            } else if (DAWN_LIKELY(pipeline_stage_uses.Contains(
-                           core::type::PipelineStageUsage::kFragmentOutput))) {
-                if (auto blend_src = attributes.blend_src) {
-                    out << " [[color(" + std::to_string(location.value()) + ") index(" +
-                               std::to_string(blend_src.value()) + ")]]";
-                } else {
-                    out << " [[color(" + std::to_string(location.value()) + ")]]";
-                }
-            } else {
-                TINT_ICE() << "invalid use of location decoration";
-            }
-        }
-
-        if (auto color = attributes.color) {
-            out << " [[color(" + std::to_string(color.value()) + ")]]";
-        }
-
-        if (auto interpolation = attributes.interpolation) {
-            auto name = InterpolationToAttribute(interpolation->type, interpolation->sampling);
-            if (name.empty()) {
-                diagnostics_.AddError(Source{}) << "unknown interpolation attribute";
-                return false;
-            }
-            out << " [[" << name << "]]";
-        }
-
-        if (attributes.invariant) {
-            invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
-            out << " " << invariant_define_name_;
-        }
-
-        out << ";";
-
-        if (is_host_shareable) {
-            // Calculate new MSL offset
-            auto size_align = MslPackedTypeSizeAndAlign(ty);
-            if (DAWN_UNLIKELY(msl_offset % size_align.align)) {
-                TINT_ICE() << "Misaligned MSL structure member " << ty->FriendlyName() << " "
-                           << mem_name;
-            }
-            msl_offset += size_align.size;
-        }
-    }
-
-    if (is_host_shareable && str->Size() != msl_offset) {
-        add_padding(str->Size() - msl_offset, msl_offset);
-    }
-
-    b->DecrementIndent();
-
-    Line(b) << "};";
-    return true;
-}
-
-bool ASTPrinter::EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr) {
-    // Handle `-e` when `e` is signed, so that we ensure that if `e` is the
-    // largest negative value, it returns `e`.
-    auto* expr_type = TypeOf(expr->expr)->UnwrapRef();
-    if (expr->op == core::UnaryOp::kNegation && expr_type->IsSignedIntegerScalarOrVector()) {
-        auto fn = tint::GetOrAdd(unary_minus_funcs_, expr_type, [&]() -> std::string {
-            // e.g.:
-            // int tint_unary_minus(const int v) {
-            //     return (v == -2147483648) ? v : -v;
-            // }
-            TextBuffer b;
-            TINT_DEFER(helpers_.Append(b));
-
-            auto fn_name = UniqueIdentifier("tint_unary_minus");
-            {
-                auto decl = Line(&b);
-                if (!EmitTypeAndName(decl, expr_type, fn_name)) {
-                    return "";
-                }
-                decl << "(const ";
-                if (!EmitType(decl, expr_type)) {
-                    return "";
-                }
-                decl << " v) {";
-            }
-
-            {
-                ScopedIndent si(&b);
-                const auto largest_negative_value =
-                    std::to_string(std::numeric_limits<int32_t>::min());
-                Line(&b) << "return select(-v, v, v == " << largest_negative_value << ");";
-            }
-            Line(&b) << "}";
-            Line(&b);
-            return fn_name;
-        });
-
-        out << fn << "(";
-        if (!EmitExpression(out, expr->expr)) {
-            return false;
-        }
-        out << ")";
-        return true;
-    }
-
-    switch (expr->op) {
-        case core::UnaryOp::kAddressOf:
-            out << "&";
-            break;
-        case core::UnaryOp::kComplement:
-            out << "~";
-            break;
-        case core::UnaryOp::kIndirection:
-            out << "*";
-            break;
-        case core::UnaryOp::kNot:
-            out << "!";
-            break;
-        case core::UnaryOp::kNegation:
-            out << "-";
-            break;
-    }
-    out << "(";
-
-    if (!EmitExpression(out, expr->expr)) {
-        return false;
-    }
-
-    out << ")";
-
-    return true;
-}
-
-bool ASTPrinter::EmitVar(const ast::Var* var) {
-    auto* sem = builder_.Sem().Get(var);
-    auto* type = sem->Type()->UnwrapRef();
-
-    auto out = Line();
-
-    switch (sem->AddressSpace()) {
-        case core::AddressSpace::kFunction:
-        case core::AddressSpace::kHandle:
-            break;
-        case core::AddressSpace::kPrivate:
-            out << "thread ";
-            break;
-        case core::AddressSpace::kWorkgroup:
-            out << "threadgroup ";
-            break;
-        default:
-            TINT_ICE() << "unhandled variable address space";
-    }
-
-    if (!EmitType(out, type)) {
-        return false;
-    }
-    out << " " << var->name->symbol.Name();
-
-    if (var->initializer != nullptr) {
-        out << " = ";
-        if (!EmitExpression(out, var->initializer)) {
-            return false;
-        }
-    } else if (sem->AddressSpace() == core::AddressSpace::kPrivate ||
-               sem->AddressSpace() == core::AddressSpace::kFunction ||
-               sem->AddressSpace() == core::AddressSpace::kUndefined) {
-        out << " = ";
-        if (!EmitZeroValue(out, type)) {
-            return false;
-        }
-    }
-    out << ";";
-
-    return true;
-}
-
-bool ASTPrinter::EmitLet(const ast::Let* let) {
-    auto* sem = builder_.Sem().Get(let);
-    auto* type = sem->Type();
-
-    auto out = Line();
-
-    switch (sem->AddressSpace()) {
-        case core::AddressSpace::kFunction:
-        case core::AddressSpace::kHandle:
-        case core::AddressSpace::kUndefined:
-            break;
-        case core::AddressSpace::kPrivate:
-            out << "thread ";
-            break;
-        case core::AddressSpace::kWorkgroup:
-            out << "threadgroup ";
-            break;
-        default:
-            TINT_ICE() << "unhandled variable address space";
-    }
-
-    if (!EmitType(out, type)) {
-        return false;
-    }
-    out << " const " << let->name->symbol.Name();
-
-    out << " = ";
-    if (!EmitExpression(out, let->initializer)) {
-        return false;
-    }
-    out << ";";
-
-    return true;
-}
-
-void ASTPrinter::IsolateUBIfNeeded() {
-    if (!options_.disable_robustness) {
-        Line() << IsolateUB();
-    }
-}
-
-std::string ASTPrinter::IsolateUB() {
-    if (isolate_ub_macro_name_.empty()) {
-        isolate_ub_macro_name_ = UniqueIdentifier("TINT_ISOLATE_UB");
-        Line(&helpers_) << "#define " << isolate_ub_macro_name_ << "(VOLATILE_NAME) \\";
-        Line(&helpers_) << "  {volatile bool VOLATILE_NAME = false; if (VOLATILE_NAME) break;}";
-        Line(&helpers_);
-    }
-    StringStream ss;
-    ss << isolate_ub_macro_name_ << "(" << UniqueIdentifier("tint_volatile_false") << ");";
-    return ss.str();
-}
-
-template <typename F>
-bool ASTPrinter::CallBuiltinHelper(StringStream& out,
-                                   const ast::CallExpression* call,
-                                   const sem::BuiltinFn* builtin,
-                                   F&& build) {
-    // Generate the helper function if it hasn't been created already
-    auto fn = tint::GetOrAdd(builtins_, builtin, [&]() -> std::string {
-        TextBuffer b;
-        TINT_DEFER(helpers_.Append(b));
-
-        auto fn_name = UniqueIdentifier(std::string("tint_") + wgsl::str(builtin->Fn()));
-        std::vector<std::string> parameter_names;
-        {
-            auto decl = Line(&b);
-            if (!EmitTypeAndName(decl, builtin->ReturnType(), fn_name)) {
-                return "";
-            }
-            {
-                ScopedParen sp(decl);
-                for (auto* param : builtin->Parameters()) {
-                    if (!parameter_names.empty()) {
-                        decl << ", ";
-                    }
-                    auto param_name = "param_" + std::to_string(parameter_names.size());
-                    if (!EmitTypeAndName(decl, param->Type(), param_name)) {
-                        return "";
-                    }
-                    parameter_names.emplace_back(std::move(param_name));
-                }
-            }
-            decl << " {";
-        }
-        {
-            ScopedIndent si(&b);
-            if (!build(&b, parameter_names)) {
-                return "";
-            }
-        }
-        Line(&b) << "}";
-        Line(&b);
-        return fn_name;
-    });
-
-    if (fn.empty()) {
-        return false;
-    }
-
-    // Call the helper
-    out << fn;
-    {
-        ScopedParen sp(out);
-        bool first = true;
-        for (auto* arg : call->args) {
-            if (!first) {
-                out << ", ";
-            }
-            first = false;
-            if (!EmitExpression(out, arg)) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-const std::string& ASTPrinter::ArrayType() {
-    if (array_template_name_.empty()) {
-        array_template_name_ = UniqueIdentifier("tint_array");
-        auto* buf = &helpers_;
-        Line(buf) << "template<typename T, size_t N>";
-        Line(buf) << "struct " << array_template_name_ << " {";
-        Line(buf) << "    const constant T& operator[](size_t i) const constant"
-                  << " { return elements[i]; }";
-        for (auto* space : {"device", "thread", "threadgroup"}) {
-            Line(buf) << "    " << space << " T& operator[](size_t i) " << space
-                      << " { return elements[i]; }";
-            Line(buf) << "    const " << space << " T& operator[](size_t i) const " << space
-                      << " { return elements[i]; }";
-        }
-        Line(buf) << "    T elements[N];";
-        Line(buf) << "};";
-        Line(buf);
-    }
-    return array_template_name_;
-}
-
-std::string ASTPrinter::StructName(const core::type::Struct* s) {
-    auto name = s->Name().Name();
-    if (HasPrefix(name, "__")) {
-        name = tint::GetOrAdd(builtin_struct_names_, s,
-                              [&] { return UniqueIdentifier(name.substr(2)); });
-    }
-    return name;
-}
-
-std::string ASTPrinter::UniqueIdentifier(const std::string& prefix /* = "" */) {
-    return builder_.Symbols().New(prefix).Name();
-}
-
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.h b/src/tint/lang/msl/writer/ast_printer/ast_printer.h
deleted file mode 100644
index 780d676..0000000
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.h
+++ /dev/null
@@ -1,482 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_MSL_WRITER_AST_PRINTER_AST_PRINTER_H_
-#define SRC_TINT_LANG_MSL_WRITER_AST_PRINTER_AST_PRINTER_H_
-
-#include <string>
-#include <tuple>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "src/tint/lang/core/builtin_value.h"
-#include "src/tint/lang/msl/writer/common/options.h"
-#include "src/tint/lang/wgsl/ast/assignment_statement.h"
-#include "src/tint/lang/wgsl/ast/binary_expression.h"
-#include "src/tint/lang/wgsl/ast/break_statement.h"
-#include "src/tint/lang/wgsl/ast/continue_statement.h"
-#include "src/tint/lang/wgsl/ast/discard_statement.h"
-#include "src/tint/lang/wgsl/ast/expression.h"
-#include "src/tint/lang/wgsl/ast/if_statement.h"
-#include "src/tint/lang/wgsl/ast/index_accessor_expression.h"
-#include "src/tint/lang/wgsl/ast/interpolate_attribute.h"
-#include "src/tint/lang/wgsl/ast/loop_statement.h"
-#include "src/tint/lang/wgsl/ast/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/ast/return_statement.h"
-#include "src/tint/lang/wgsl/ast/switch_statement.h"
-#include "src/tint/lang/wgsl/ast/unary_op_expression.h"
-#include "src/tint/lang/wgsl/program/program.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/utils/containers/scope_stack.h"
-#include "src/tint/utils/text/string_stream.h"
-#include "src/tint/utils/text_generator.h"
-
-namespace tint::sem {
-class BuiltinFn;
-class Call;
-class ValueConstructor;
-class ValueConversion;
-}  // namespace tint::sem
-
-namespace tint::msl::writer {
-
-/// The result of sanitizing a program for generation.
-struct SanitizedResult {
-    /// Constructor
-    SanitizedResult();
-    /// Destructor
-    ~SanitizedResult();
-    /// Move constructor
-    SanitizedResult(SanitizedResult&&);
-
-    /// The sanitized program.
-    Program program;
-    /// True if the shader needs a UBO of buffer sizes.
-    bool needs_storage_buffer_sizes = false;
-};
-
-/// Sanitize a program in preparation for generating MSL.
-/// @param program The program to sanitize
-/// @param options The MSL generator options.
-/// @returns the sanitized program and any supplementary information
-SanitizedResult Sanitize(const Program& program, const Options& options);
-
-/// Implementation class for MSL generator
-class ASTPrinter : public tint::TextGenerator {
-  public:
-    /// Constructor
-    /// @param program the program to generate
-    /// @param options emission options
-    explicit ASTPrinter(const Program& program, const Options& options = {});
-    ~ASTPrinter() override;
-
-    /// @returns true on successful generation; false otherwise
-    bool Generate();
-
-    /// @returns true if an invariant attribute was generated
-    bool HasInvariant() { return !invariant_define_name_.empty(); }
-
-    /// @returns a map from entry point to list of required workgroup allocations
-    const std::unordered_map<std::string, std::vector<uint32_t>>& DynamicWorkgroupAllocations()
-        const {
-        return workgroup_allocations_;
-    }
-
-    /// Handles generating a declared type
-    /// @param ty the declared type to generate
-    /// @returns true if the declared type was emitted
-    bool EmitTypeDecl(const core::type::Type* ty);
-    /// Handles an index accessor expression
-    /// @param out the output of the expression stream
-    /// @param expr the expression to emit
-    /// @returns true if the index accessor was emitted
-    bool EmitIndexAccessor(StringStream& out, const ast::IndexAccessorExpression* expr);
-    /// Handles an assignment statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitAssign(const ast::AssignmentStatement* stmt);
-    /// Handles generating a binary expression
-    /// @param out the output of the expression stream
-    /// @param expr the binary expression
-    /// @returns true if the expression was emitted, false otherwise
-    bool EmitBinary(StringStream& out, const ast::BinaryExpression* expr);
-    /// Handles generating a bitcast expression
-    /// @param out the output of the expression stream
-    /// @param expr the bitcast expression
-    /// @returns true if the bitcast was emitted
-    bool EmitBitcastCall(StringStream& out, const ast::CallExpression* expr);
-    /// Handles a block statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBlock(const ast::BlockStatement* stmt);
-    /// Handles a break statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBreak(const ast::BreakStatement* stmt);
-    /// Handles a break-if statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBreakIf(const ast::BreakIfStatement* stmt);
-    /// Handles generating a call expression
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @returns true if the call expression is emitted
-    bool EmitCall(StringStream& out, const ast::CallExpression* expr);
-    /// Handles generating a builtin call expression
-    /// @param out the output of the expression stream
-    /// @param call the call expression
-    /// @param builtin the builtin being called
-    /// @returns true if the call expression is emitted
-    bool EmitBuiltinCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn* builtin);
-    /// Handles generating a value conversion expression
-    /// @param out the output of the expression stream
-    /// @param call the call expression
-    /// @param conv the value conversion
-    /// @returns true if the expression is emitted
-    bool EmitTypeConversion(StringStream& out,
-                            const sem::Call* call,
-                            const sem::ValueConversion* conv);
-    /// Handles generating a value constructor
-    /// @param out the output of the expression stream
-    /// @param call the call expression
-    /// @param ctor the value constructor
-    /// @returns true if the initializer is emitted
-    bool EmitTypeInitializer(StringStream& out,
-                             const sem::Call* call,
-                             const sem::ValueConstructor* ctor);
-    /// Handles generating a function call
-    /// @param out the output of the expression stream
-    /// @param call the call expression
-    /// @param func the target function
-    /// @returns true if the call is emitted
-    bool EmitFunctionCall(StringStream& out, const sem::Call* call, const sem::Function* func);
-    /// Handles generating a call to an atomic function (`atomicAdd`,
-    /// `atomicMax`, etc)
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the atomic builtin
-    /// @returns true if the call expression is emitted
-    bool EmitAtomicCall(StringStream& out,
-                        const ast::CallExpression* expr,
-                        const sem::BuiltinFn* builtin);
-    /// Handles generating a call to a texture function (`textureSample`,
-    /// `textureSampleGrad`, etc)
-    /// @param out the output of the expression stream
-    /// @param call the call expression
-    /// @param builtin the semantic information for the texture builtin
-    /// @returns true if the call expression is emitted
-    bool EmitTextureCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `dot()` builtin
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitDotCall(StringStream& out,
-                     const ast::CallExpression* expr,
-                     const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `modf()` builtin
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitModfCall(StringStream& out,
-                      const ast::CallExpression* expr,
-                      const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `frexp()` builtin
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitFrexpCall(StringStream& out,
-                       const ast::CallExpression* expr,
-                       const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `degrees()` builtin
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitDegreesCall(StringStream& out,
-                         const ast::CallExpression* expr,
-                         const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `radians()` builtin
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitRadiansCall(StringStream& out,
-                         const ast::CallExpression* expr,
-                         const sem::BuiltinFn* builtin);
-    /// Handles a case statement
-    /// @param stmt the statement
-    /// @returns true if the statement was emitted successfully
-    bool EmitCase(const ast::CaseStatement* stmt);
-    /// Handles a continue statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitContinue(const ast::ContinueStatement* stmt);
-    /// Handles generating a discard statement
-    /// @param stmt the discard statement
-    /// @returns true if the statement was successfully emitted
-    bool EmitDiscard(const ast::DiscardStatement* stmt);
-    /// Handles emitting the entry point function
-    /// @param func the entry point function
-    /// @returns true if the entry point function was emitted
-    bool EmitEntryPointFunction(const ast::Function* func);
-    /// Handles generate an Expression
-    /// @param out the output of the expression stream
-    /// @param expr the expression
-    /// @returns true if the expression was emitted
-    bool EmitExpression(StringStream& out, const ast::Expression* expr);
-    /// Handles generating a function
-    /// @param func the function to generate
-    /// @returns true if the function was emitted
-    bool EmitFunction(const ast::Function* func);
-    /// Handles generating an identifier expression
-    /// @param out the output of the expression stream
-    /// @param expr the identifier expression
-    /// @returns true if the identifier was emitted
-    bool EmitIdentifier(StringStream& out, const ast::IdentifierExpression* expr);
-    /// Handles an if statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitIf(const ast::IfStatement* stmt);
-    /// Handles a constant value
-    /// @param out the output stream
-    /// @param constant the constant value to emit
-    /// @returns true if the constant value was successfully emitted
-    bool EmitConstant(StringStream& out, const core::constant::Value* constant);
-    /// Handles a literal
-    /// @param out the output of the expression stream
-    /// @param lit the literal to emit
-    /// @returns true if the literal was successfully emitted
-    bool EmitLiteral(StringStream& out, const ast::LiteralExpression* lit);
-    /// Handles a loop statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitLoop(const ast::LoopStatement* stmt);
-    /// Handles a for loop statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitForLoop(const ast::ForLoopStatement* stmt);
-    /// Handles a while statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitWhile(const ast::WhileStatement* stmt);
-    /// Handles a member accessor expression
-    /// @param out the output of the expression stream
-    /// @param expr the member accessor expression
-    /// @returns true if the member accessor was emitted
-    bool EmitMemberAccessor(StringStream& out, const ast::MemberAccessorExpression* expr);
-    /// Handles return statements
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitReturn(const ast::ReturnStatement* stmt);
-    /// Handles emitting a pipeline stage name
-    /// @param out the output of the expression stream
-    /// @param stage the stage to emit
-    void EmitStage(StringStream& out, ast::PipelineStage stage);
-    /// Handles statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitStatement(const ast::Statement* stmt);
-    /// Emits a list of statements
-    /// @param stmts the statement list
-    /// @returns true if the statements were emitted successfully
-    bool EmitStatements(VectorRef<const ast::Statement*> stmts);
-    /// Emits a list of statements with an indentation
-    /// @param stmts the statement list
-    /// @returns true if the statements were emitted successfully
-    bool EmitStatementsWithIndent(VectorRef<const ast::Statement*> stmts);
-    /// Handles generating a switch statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitSwitch(const ast::SwitchStatement* stmt);
-    /// Handles generating a type
-    /// @param out the output of the type stream
-    /// @param type the type to generate
-    /// @returns true if the type is emitted
-    bool EmitType(StringStream& out, const core::type::Type* type);
-    /// Handles generating type and name
-    /// @param out the output stream
-    /// @param type the type to generate
-    /// @param name the name to emit
-    /// @returns true if the type is emitted
-    bool EmitTypeAndName(StringStream& out, const core::type::Type* type, const std::string& name);
-    /// Handles generating a address space
-    /// @param out the output of the type stream
-    /// @param sc the address space to generate
-    /// @returns true if the address space is emitted
-    bool EmitAddressSpace(StringStream& out, core::AddressSpace sc);
-    /// Handles generating a struct declaration. If the structure has already been emitted, then
-    /// this function will simply return `true` without emitting anything.
-    /// @param buffer the text buffer that the type declaration will be written to
-    /// @param str the struct to generate
-    /// @returns true if the struct is emitted
-    bool EmitStructType(TextBuffer* buffer, const core::type::Struct* str);
-    /// Handles a unary op expression
-    /// @param out the output of the expression stream
-    /// @param expr the expression to emit
-    /// @returns true if the expression was emitted
-    bool EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr);
-    /// Handles generating a 'var' declaration
-    /// @param var the variable to generate
-    /// @returns true if the variable was emitted
-    bool EmitVar(const ast::Var* var);
-    /// Handles generating a 'let' declaration
-    /// @param let the variable to generate
-    /// @returns true if the variable was emitted
-    bool EmitLet(const ast::Let* let);
-    /// Emits the zero value for the given type
-    /// @param out the output of the expression stream
-    /// @param type the type to emit the value for
-    /// @returns true if the zero value was successfully emitted.
-    bool EmitZeroValue(StringStream& out, const core::type::Type* type);
-    /// Handles generating a call to the `dot4I8Packed()` builtin
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitDot4I8PackedCall(StringStream& out,
-                              const ast::CallExpression* expr,
-                              const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `dot4U8Packed()` builtin
-    /// @param out the output of the expression stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitDot4U8PackedCall(StringStream& out,
-                              const ast::CallExpression* expr,
-                              const sem::BuiltinFn* builtin);
-
-    /// If robustness is required, emits code to prevent the MSL compiler
-    /// from believing the loop is infinite.
-    /// Otherwise does nothing.
-    void IsolateUBIfNeeded();
-
-    /// Lazily generates the TINT_ISOLATE_UB macro, and returns a call to
-    /// the macro, passing in a unique identifier. The call tricks the MSL
-    /// compiler into thinking it might execute a `break`, but otherwise
-    /// has no effect in the generated code.
-    ///
-    /// Invoke this inside the body of a loop to prevent the MSL compiler
-    /// from inferring the loop never terminates.
-    /// @return the MSL to call the TINT_ISOLATE_UB macro.
-    std::string IsolateUB();
-
-    /// Handles generating a builtin name
-    /// @param builtin the semantic info for the builtin
-    /// @returns the name or "" if not valid
-    std::string generate_builtin_name(const sem::BuiltinFn* builtin);
-
-  private:
-    /// CallBuiltinHelper will call the builtin helper function, creating it
-    /// if it hasn't been built already. If the builtin needs to be built then
-    /// CallBuiltinHelper will generate the function signature and will call
-    /// `build` to emit the body of the function.
-    /// @param out the output of the expression stream
-    /// @param call the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @param build a function with the signature:
-    ///        `bool(TextBuffer* buffer, const std::vector<std::string>& params)`
-    ///        Where:
-    ///          `buffer` is the body of the generated function
-    ///          `params` is the name of all the generated function parameters
-    /// @returns true if the call expression is emitted
-    template <typename F>
-    bool CallBuiltinHelper(StringStream& out,
-                           const ast::CallExpression* call,
-                           const sem::BuiltinFn* builtin,
-                           F&& build);
-
-    /// @returns the name of the templated tint_array helper type, generating it if this is the
-    /// first call.
-    const std::string& ArrayType();
-
-    /// @param s the structure
-    /// @returns the name of the structure, taking special care of builtin structures that start
-    /// with double underscores. If the structure is a builtin, then the returned name will be a
-    /// unique name without the leading underscores.
-    std::string StructName(const core::type::Struct* s);
-
-    /// @return a new, unique identifier with the given prefix.
-    /// @param prefix optional prefix to apply to the generated identifier. If empty "tint_symbol"
-    /// will be used.
-    std::string UniqueIdentifier(const std::string& prefix = "");
-
-    /// Alias for builder_.TypeOf(ptr)
-    template <typename T>
-    auto TypeOf(T* ptr) {
-        return builder_.TypeOf(ptr);
-    }
-
-    ProgramBuilder builder_;
-    Options options_;
-
-    TextBuffer helpers_;  // Helper functions emitted at the top of the output
-
-    /// Map of builtin structure to unique generated name
-    std::unordered_map<const core::type::Struct*, std::string> builtin_struct_names_;
-
-    std::function<bool()> emit_continuing_;
-
-    /// The name of the macro used to prevent UB affecting later control flow.
-    /// Do not use this directly, instead call IsolateUB().
-    std::string isolate_ub_macro_name_;
-
-    /// Name of atomicCompareExchangeWeak() helper for the given pointer storage
-    /// class and struct return type
-    using ACEWKeyType =
-        tint::UnorderedKeyWrapper<std::tuple<core::AddressSpace, const core::type::Struct*>>;
-    std::unordered_map<ACEWKeyType, std::string> atomicCompareExchangeWeak_;
-
-    /// Unique name of the 'TINT_INVARIANT' preprocessor define.
-    /// Non-empty only if an invariant attribute has been generated.
-    std::string invariant_define_name_;
-
-    /// The generated name for the packed vec3 type.
-    std::string packed_vec3_ty_;
-
-    /// Unique name of the tint_array<T, N> template.
-    /// Non-empty only if the template has been generated.
-    std::string array_template_name_;
-
-    /// A map from entry point name to a list of dynamic workgroup allocations.
-    /// Each entry in the vector is the size of the workgroup allocation that
-    /// should be created for that index.
-    std::unordered_map<std::string, std::vector<uint32_t>> workgroup_allocations_;
-
-    std::unordered_map<const sem::BuiltinFn*, std::string> builtins_;
-    std::unordered_map<const core::type::Type*, std::string> unary_minus_funcs_;
-    std::unordered_map<uint32_t, std::string> int_dot_funcs_;
-    std::unordered_set<const core::type::Struct*> emitted_structs_;
-};
-
-}  // namespace tint::msl::writer
-
-#endif  // SRC_TINT_LANG_MSL_WRITER_AST_PRINTER_AST_PRINTER_H_
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer_test.cc b/src/tint/lang/msl/writer/ast_printer/ast_printer_test.cc
deleted file mode 100644
index 5548533..0000000
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer_test.cc
+++ /dev/null
@@ -1,531 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/msl/writer/writer.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, InvalidProgram) {
-    Diagnostics().AddError(Source{}) << "make the program invalid";
-    ASSERT_FALSE(IsValid());
-    auto program = resolver::Resolve(*this);
-    ASSERT_FALSE(program.IsValid());
-    auto result = Generate(program, Options{});
-    EXPECT_NE(result, Success);
-    EXPECT_EQ(result.Failure().reason.Str(), "error: make the program invalid");
-}
-
-TEST_F(MslASTPrinterTest, UnsupportedExtension) {
-    Enable(Source{{12, 34}}, wgsl::Extension::kChromiumExperimentalPushConstant);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_FALSE(gen.Generate());
-    EXPECT_EQ(
-        gen.Diagnostics().Str(),
-        R"(12:34 error: MSL backend does not support extension 'chromium_experimental_push_constant')");
-}
-
-TEST_F(MslASTPrinterTest, RequiresDirective) {
-    Require(wgsl::LanguageFeature::kReadonlyAndReadwriteStorageTextures);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-)");
-}
-
-TEST_F(MslASTPrinterTest, Generate) {
-    Func("my_func", tint::Empty, ty.void_(), tint::Empty,
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-kernel void my_func() {
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, HasInvariantAttribute_True) {
-    auto* out = Structure("Out", Vector{
-                                     Member("pos", ty.vec4<f32>(),
-                                            Vector{
-                                                Builtin(core::BuiltinValue::kPosition),
-                                                Invariant(),
-                                            }),
-                                 });
-    Func("vert_main", tint::Empty, ty.Of(out), Vector{Return(Call(ty.Of(out)))},
-         Vector{
-             Stage(ast::PipelineStage::kVertex),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_TRUE(gen.HasInvariant());
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-#if __METAL_VERSION__ >= 210
-#define TINT_INVARIANT [[invariant]]
-#else
-#define TINT_INVARIANT
-#endif
-
-struct Out {
-  float4 pos [[position]] TINT_INVARIANT;
-};
-
-vertex Out vert_main() {
-  return Out{};
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, HasInvariantAttribute_False) {
-    auto* out = Structure("Out", Vector{
-                                     Member("pos", ty.vec4<f32>(),
-                                            Vector{
-                                                Builtin(core::BuiltinValue::kPosition),
-                                            }),
-                                 });
-    Func("vert_main", tint::Empty, ty.Of(out), Vector{Return(Call(ty.Of(out)))},
-         Vector{
-             Stage(ast::PipelineStage::kVertex),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_FALSE(gen.HasInvariant());
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct Out {
-  float4 pos [[position]];
-};
-
-vertex Out vert_main() {
-  return Out{};
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, WorkgroupMatrix) {
-    GlobalVar("m", ty.mat2x2<f32>(), core::AddressSpace::kWorkgroup);
-    Func("comp_main", tint::Empty, ty.void_(), Vector{Decl(Let("x", Expr("m")))},
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct tint_symbol_4 {
-  float2x2 m;
-};
-
-void tint_zero_workgroup_memory(uint local_idx, threadgroup float2x2* const tint_symbol) {
-  if ((local_idx < 1u)) {
-    *(tint_symbol) = float2x2(float2(0.0f), float2(0.0f));
-  }
-  threadgroup_barrier(mem_flags::mem_threadgroup);
-}
-
-void comp_main_inner(uint local_invocation_index, threadgroup float2x2* const tint_symbol_1) {
-  tint_zero_workgroup_memory(local_invocation_index, tint_symbol_1);
-  float2x2 const x = *(tint_symbol_1);
-}
-
-kernel void comp_main(threadgroup tint_symbol_4* tint_symbol_3 [[threadgroup(0)]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup float2x2* const tint_symbol_2 = &((*(tint_symbol_3)).m);
-  comp_main_inner(local_invocation_index, tint_symbol_2);
-  return;
-}
-
-)");
-
-    auto allocations = gen.DynamicWorkgroupAllocations();
-    ASSERT_TRUE(allocations.count("comp_main"));
-    ASSERT_EQ(allocations.at("comp_main").size(), 1u);
-    EXPECT_EQ(allocations.at("comp_main")[0], 2u * 2u * sizeof(float));
-}
-
-TEST_F(MslASTPrinterTest, WorkgroupMatrixInArray) {
-    GlobalVar("m", ty.array(ty.mat2x2<f32>(), 4_i), core::AddressSpace::kWorkgroup);
-    Func("comp_main", tint::Empty, ty.void_(), Vector{Decl(Let("x", Expr("m")))},
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-struct tint_symbol_4 {
-  tint_array<float2x2, 4> m;
-};
-
-void tint_zero_workgroup_memory(uint local_idx, threadgroup tint_array<float2x2, 4>* const tint_symbol) {
-  for(uint idx = local_idx; (idx < 4u); idx = (idx + 1u)) {
-    uint const i = idx;
-    (*(tint_symbol))[i] = float2x2(float2(0.0f), float2(0.0f));
-  }
-  threadgroup_barrier(mem_flags::mem_threadgroup);
-}
-
-void comp_main_inner(uint local_invocation_index, threadgroup tint_array<float2x2, 4>* const tint_symbol_1) {
-  tint_zero_workgroup_memory(local_invocation_index, tint_symbol_1);
-  tint_array<float2x2, 4> const x = *(tint_symbol_1);
-}
-
-kernel void comp_main(threadgroup tint_symbol_4* tint_symbol_3 [[threadgroup(0)]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup tint_array<float2x2, 4>* const tint_symbol_2 = &((*(tint_symbol_3)).m);
-  comp_main_inner(local_invocation_index, tint_symbol_2);
-  return;
-}
-
-)");
-
-    auto allocations = gen.DynamicWorkgroupAllocations();
-    ASSERT_TRUE(allocations.count("comp_main"));
-    ASSERT_EQ(allocations.at("comp_main").size(), 1u);
-    EXPECT_EQ(allocations.at("comp_main")[0], 4u * 2u * 2u * sizeof(float));
-}
-
-TEST_F(MslASTPrinterTest, WorkgroupMatrixInStruct) {
-    Structure("S1", Vector{
-                        Member("m1", ty.mat2x2<f32>()),
-                        Member("m2", ty.mat4x4<f32>()),
-                    });
-    Structure("S2", Vector{
-                        Member("s", ty("S1")),
-                    });
-    GlobalVar("s", ty("S2"), core::AddressSpace::kWorkgroup);
-    Func("comp_main", tint::Empty, ty.void_(), Vector{Decl(Let("x", Expr("s")))},
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-struct S1 {
-  float2x2 m1;
-  float4x4 m2;
-};
-
-struct S2 {
-  S1 s;
-};
-
-struct tint_symbol_5 {
-  S2 s;
-};
-
-void tint_zero_workgroup_memory(uint local_idx, threadgroup S2* const tint_symbol_1) {
-  if ((local_idx < 1u)) {
-    S2 const tint_symbol = S2{};
-    *(tint_symbol_1) = tint_symbol;
-  }
-  threadgroup_barrier(mem_flags::mem_threadgroup);
-}
-
-void comp_main_inner(uint local_invocation_index, threadgroup S2* const tint_symbol_2) {
-  tint_zero_workgroup_memory(local_invocation_index, tint_symbol_2);
-  S2 const x = *(tint_symbol_2);
-}
-
-kernel void comp_main(threadgroup tint_symbol_5* tint_symbol_4 [[threadgroup(0)]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup S2* const tint_symbol_3 = &((*(tint_symbol_4)).s);
-  comp_main_inner(local_invocation_index, tint_symbol_3);
-  return;
-}
-
-)");
-
-    auto allocations = gen.DynamicWorkgroupAllocations();
-    ASSERT_TRUE(allocations.count("comp_main"));
-    ASSERT_EQ(allocations.at("comp_main").size(), 1u);
-    EXPECT_EQ(allocations.at("comp_main")[0], (2 * 2 * sizeof(float)) + (4u * 4u * sizeof(float)));
-}
-
-TEST_F(MslASTPrinterTest, WorkgroupMatrix_Multiples) {
-    GlobalVar("m1", ty.mat2x2<f32>(), core::AddressSpace::kWorkgroup);
-    GlobalVar("m2", ty.mat2x3<f32>(), core::AddressSpace::kWorkgroup);
-    GlobalVar("m3", ty.mat2x4<f32>(), core::AddressSpace::kWorkgroup);
-    GlobalVar("m4", ty.mat3x2<f32>(), core::AddressSpace::kWorkgroup);
-    GlobalVar("m5", ty.mat3x3<f32>(), core::AddressSpace::kWorkgroup);
-    GlobalVar("m6", ty.mat3x4<f32>(), core::AddressSpace::kWorkgroup);
-    GlobalVar("m7", ty.mat4x2<f32>(), core::AddressSpace::kWorkgroup);
-    GlobalVar("m8", ty.mat4x3<f32>(), core::AddressSpace::kWorkgroup);
-    GlobalVar("m9", ty.mat4x4<f32>(), core::AddressSpace::kWorkgroup);
-    Func("main1", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Let("a1", Expr("m1"))),
-             Decl(Let("a2", Expr("m2"))),
-             Decl(Let("a3", Expr("m3"))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-    Func("main2", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Let("a1", Expr("m4"))),
-             Decl(Let("a2", Expr("m5"))),
-             Decl(Let("a3", Expr("m6"))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-    Func("main3", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Let("a1", Expr("m7"))),
-             Decl(Let("a2", Expr("m8"))),
-             Decl(Let("a3", Expr("m9"))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-    Func("main4_no_usages", tint::Empty, ty.void_(), tint::Empty,
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-struct tint_symbol_16 {
-  float2x2 m1;
-  float2x4 m3;
-};
-
-struct tint_symbol_24 {
-  float3x2 m4;
-  float3x4 m6;
-};
-
-struct tint_symbol_32 {
-  float4x2 m7;
-  float4x4 m9;
-};
-
-struct tint_packed_vec3_f32_array_element {
-  packed_float3 elements;
-};
-
-float2x3 tint_unpack_vec3_in_composite(tint_array<tint_packed_vec3_f32_array_element, 2> in) {
-  float2x3 result = float2x3(float3(in[0].elements), float3(in[1].elements));
-  return result;
-}
-
-float3x3 tint_unpack_vec3_in_composite_1(tint_array<tint_packed_vec3_f32_array_element, 3> in) {
-  float3x3 result = float3x3(float3(in[0].elements), float3(in[1].elements), float3(in[2].elements));
-  return result;
-}
-
-float4x3 tint_unpack_vec3_in_composite_2(tint_array<tint_packed_vec3_f32_array_element, 4> in) {
-  float4x3 result = float4x3(float3(in[0].elements), float3(in[1].elements), float3(in[2].elements), float3(in[3].elements));
-  return result;
-}
-
-tint_array<tint_packed_vec3_f32_array_element, 2> tint_pack_vec3_in_composite(float2x3 in) {
-  tint_array<tint_packed_vec3_f32_array_element, 2> result = tint_array<tint_packed_vec3_f32_array_element, 2>{tint_packed_vec3_f32_array_element{.elements=packed_float3(in[0])}, tint_packed_vec3_f32_array_element{.elements=packed_float3(in[1])}};
-  return result;
-}
-
-tint_array<tint_packed_vec3_f32_array_element, 3> tint_pack_vec3_in_composite_1(float3x3 in) {
-  tint_array<tint_packed_vec3_f32_array_element, 3> result = tint_array<tint_packed_vec3_f32_array_element, 3>{tint_packed_vec3_f32_array_element{.elements=packed_float3(in[0])}, tint_packed_vec3_f32_array_element{.elements=packed_float3(in[1])}, tint_packed_vec3_f32_array_element{.elements=packed_float3(in[2])}};
-  return result;
-}
-
-tint_array<tint_packed_vec3_f32_array_element, 4> tint_pack_vec3_in_composite_2(float4x3 in) {
-  tint_array<tint_packed_vec3_f32_array_element, 4> result = tint_array<tint_packed_vec3_f32_array_element, 4>{tint_packed_vec3_f32_array_element{.elements=packed_float3(in[0])}, tint_packed_vec3_f32_array_element{.elements=packed_float3(in[1])}, tint_packed_vec3_f32_array_element{.elements=packed_float3(in[2])}, tint_packed_vec3_f32_array_element{.elements=packed_float3(in[3])}};
-  return result;
-}
-
-void tint_zero_workgroup_memory(uint local_idx, threadgroup float2x2* const tint_symbol, threadgroup tint_array<tint_packed_vec3_f32_array_element, 2>* const tint_symbol_1, threadgroup float2x4* const tint_symbol_2) {
-  if ((local_idx < 1u)) {
-    *(tint_symbol) = float2x2(float2(0.0f), float2(0.0f));
-    *(tint_symbol_1) = tint_pack_vec3_in_composite(float2x3(float3(0.0f), float3(0.0f)));
-    *(tint_symbol_2) = float2x4(float4(0.0f), float4(0.0f));
-  }
-  threadgroup_barrier(mem_flags::mem_threadgroup);
-}
-
-void tint_zero_workgroup_memory_1(uint local_idx_1, threadgroup float3x2* const tint_symbol_3, threadgroup tint_array<tint_packed_vec3_f32_array_element, 3>* const tint_symbol_4, threadgroup float3x4* const tint_symbol_5) {
-  if ((local_idx_1 < 1u)) {
-    *(tint_symbol_3) = float3x2(float2(0.0f), float2(0.0f), float2(0.0f));
-    *(tint_symbol_4) = tint_pack_vec3_in_composite_1(float3x3(float3(0.0f), float3(0.0f), float3(0.0f)));
-    *(tint_symbol_5) = float3x4(float4(0.0f), float4(0.0f), float4(0.0f));
-  }
-  threadgroup_barrier(mem_flags::mem_threadgroup);
-}
-
-void tint_zero_workgroup_memory_2(uint local_idx_2, threadgroup float4x2* const tint_symbol_6, threadgroup tint_array<tint_packed_vec3_f32_array_element, 4>* const tint_symbol_7, threadgroup float4x4* const tint_symbol_8) {
-  if ((local_idx_2 < 1u)) {
-    *(tint_symbol_6) = float4x2(float2(0.0f), float2(0.0f), float2(0.0f), float2(0.0f));
-    *(tint_symbol_7) = tint_pack_vec3_in_composite_2(float4x3(float3(0.0f), float3(0.0f), float3(0.0f), float3(0.0f)));
-    *(tint_symbol_8) = float4x4(float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f));
-  }
-  threadgroup_barrier(mem_flags::mem_threadgroup);
-}
-
-void main1_inner(uint local_invocation_index, threadgroup float2x2* const tint_symbol_9, threadgroup tint_array<tint_packed_vec3_f32_array_element, 2>* const tint_symbol_10, threadgroup float2x4* const tint_symbol_11) {
-  tint_zero_workgroup_memory(local_invocation_index, tint_symbol_9, tint_symbol_10, tint_symbol_11);
-  float2x2 const a1 = *(tint_symbol_9);
-  float2x3 const a2 = tint_unpack_vec3_in_composite(*(tint_symbol_10));
-  float2x4 const a3 = *(tint_symbol_11);
-}
-
-kernel void main1(threadgroup tint_symbol_16* tint_symbol_13 [[threadgroup(0)]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup float2x2* const tint_symbol_12 = &((*(tint_symbol_13)).m1);
-  threadgroup tint_array<tint_packed_vec3_f32_array_element, 2> tint_symbol_14;
-  threadgroup float2x4* const tint_symbol_15 = &((*(tint_symbol_13)).m3);
-  main1_inner(local_invocation_index, tint_symbol_12, &(tint_symbol_14), tint_symbol_15);
-  return;
-}
-
-void main2_inner(uint local_invocation_index_1, threadgroup float3x2* const tint_symbol_17, threadgroup tint_array<tint_packed_vec3_f32_array_element, 3>* const tint_symbol_18, threadgroup float3x4* const tint_symbol_19) {
-  tint_zero_workgroup_memory_1(local_invocation_index_1, tint_symbol_17, tint_symbol_18, tint_symbol_19);
-  float3x2 const a1 = *(tint_symbol_17);
-  float3x3 const a2 = tint_unpack_vec3_in_composite_1(*(tint_symbol_18));
-  float3x4 const a3 = *(tint_symbol_19);
-}
-
-kernel void main2(threadgroup tint_symbol_24* tint_symbol_21 [[threadgroup(0)]], uint local_invocation_index_1 [[thread_index_in_threadgroup]]) {
-  threadgroup float3x2* const tint_symbol_20 = &((*(tint_symbol_21)).m4);
-  threadgroup tint_array<tint_packed_vec3_f32_array_element, 3> tint_symbol_22;
-  threadgroup float3x4* const tint_symbol_23 = &((*(tint_symbol_21)).m6);
-  main2_inner(local_invocation_index_1, tint_symbol_20, &(tint_symbol_22), tint_symbol_23);
-  return;
-}
-
-void main3_inner(uint local_invocation_index_2, threadgroup float4x2* const tint_symbol_25, threadgroup tint_array<tint_packed_vec3_f32_array_element, 4>* const tint_symbol_26, threadgroup float4x4* const tint_symbol_27) {
-  tint_zero_workgroup_memory_2(local_invocation_index_2, tint_symbol_25, tint_symbol_26, tint_symbol_27);
-  float4x2 const a1 = *(tint_symbol_25);
-  float4x3 const a2 = tint_unpack_vec3_in_composite_2(*(tint_symbol_26));
-  float4x4 const a3 = *(tint_symbol_27);
-}
-
-kernel void main3(threadgroup tint_symbol_32* tint_symbol_29 [[threadgroup(0)]], uint local_invocation_index_2 [[thread_index_in_threadgroup]]) {
-  threadgroup float4x2* const tint_symbol_28 = &((*(tint_symbol_29)).m7);
-  threadgroup tint_array<tint_packed_vec3_f32_array_element, 4> tint_symbol_30;
-  threadgroup float4x4* const tint_symbol_31 = &((*(tint_symbol_29)).m9);
-  main3_inner(local_invocation_index_2, tint_symbol_28, &(tint_symbol_30), tint_symbol_31);
-  return;
-}
-
-kernel void main4_no_usages() {
-  return;
-}
-
-)");
-
-    auto allocations = gen.DynamicWorkgroupAllocations();
-    ASSERT_TRUE(allocations.count("main1"));
-    ASSERT_TRUE(allocations.count("main2"));
-    ASSERT_TRUE(allocations.count("main3"));
-    ASSERT_EQ(allocations.at("main1").size(), 1u);
-    EXPECT_EQ(allocations.at("main1")[0], 12u * sizeof(float));
-    ASSERT_EQ(allocations.at("main2").size(), 1u);
-    EXPECT_EQ(allocations.at("main2")[0], 20u * sizeof(float));
-    ASSERT_EQ(allocations.at("main3").size(), 1u);
-    EXPECT_EQ(allocations.at("main3")[0], 24u * sizeof(float));
-    EXPECT_EQ(allocations.at("main4_no_usages").size(), 0u);
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_type_test.cc b/src/tint/lang/msl/writer/ast_printer/ast_type_test.cc
deleted file mode 100644
index e39b685..0000000
--- a/src/tint/lang/msl/writer/ast_printer/ast_type_test.cc
+++ /dev/null
@@ -1,932 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <array>
-
-#include "gmock/gmock.h"
-
-#include "src/tint/lang/core/type/depth_multisampled_texture.h"
-#include "src/tint/lang/core/type/depth_texture.h"
-#include "src/tint/lang/core/type/multisampled_texture.h"
-#include "src/tint/lang/core/type/sampled_texture.h"
-#include "src/tint/lang/core/type/sampler.h"
-#include "src/tint/lang/core/type/storage_texture.h"
-#include "src/tint/lang/core/type/texture_dimension.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-// Done except for two questions on:
-// TEST_F(MslASTPrinterTest, EmitType_Struct_WithAttribute) {
-// TEST_F(MslASTPrinterTest, EmitType_Pointer) {
-
-namespace tint::msl::writer {
-namespace {
-
-using ::testing::HasSubstr;
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-void FormatMSLField(StringStream& out,
-                    const char* addr,
-                    const char* type,
-                    size_t array_count,
-                    const char* name) {
-    out << "  /* " << std::string(addr) << " */ ";
-    if (array_count == 0) {
-        out << type << " ";
-    } else {
-        out << "tint_array<" << type << ", " << std::to_string(array_count) << "> ";
-    }
-    out << name << ";\n";
-}
-
-#define CHECK_TYPE_SIZE_AND_ALIGN(TYPE, SIZE, ALIGN)      \
-    static_assert(sizeof(TYPE) == SIZE, "Bad type size"); \
-    static_assert(alignof(TYPE) == ALIGN, "Bad type alignment")
-
-// Declare C++ types that match the size and alignment of the types of the same
-// name in MSL.
-#define DECLARE_TYPE(NAME, SIZE, ALIGN) \
-    struct alignas(ALIGN) NAME {        \
-        uint8_t _[SIZE];                \
-    };                                  \
-    CHECK_TYPE_SIZE_AND_ALIGN(NAME, SIZE, ALIGN)
-
-// Size and alignments taken from the MSL spec:
-// https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
-DECLARE_TYPE(float2, 8, 8);
-DECLARE_TYPE(float3, 12, 4);
-DECLARE_TYPE(float4, 16, 16);
-DECLARE_TYPE(float2x2, 16, 8);
-DECLARE_TYPE(float2x3, 32, 16);
-DECLARE_TYPE(float2x4, 32, 16);
-DECLARE_TYPE(float3x2, 24, 8);
-DECLARE_TYPE(float3x3, 48, 16);
-DECLARE_TYPE(float3x4, 48, 16);
-DECLARE_TYPE(float4x2, 32, 8);
-DECLARE_TYPE(float4x3, 64, 16);
-DECLARE_TYPE(float4x4, 64, 16);
-DECLARE_TYPE(half2, 4, 4);
-DECLARE_TYPE(packed_half3, 6, 2);
-DECLARE_TYPE(half4, 8, 8);
-DECLARE_TYPE(half2x2, 8, 4);
-DECLARE_TYPE(half2x3, 16, 8);
-DECLARE_TYPE(half2x4, 16, 8);
-DECLARE_TYPE(half3x2, 12, 4);
-DECLARE_TYPE(half3x3, 24, 8);
-DECLARE_TYPE(half3x4, 24, 8);
-DECLARE_TYPE(half4x2, 16, 4);
-DECLARE_TYPE(half4x3, 32, 8);
-DECLARE_TYPE(half4x4, 32, 8);
-using uint = unsigned int;
-
-using MslASTPrinterTest = TestHelper;
-
-// MslPrinterTest.EmitType_Array
-TEST_F(MslASTPrinterTest, EmitType_Array) {
-    auto arr = ty.array<bool, 4>();
-    ast::Type type = GlobalVar("G", arr, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type))) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "tint_array<bool, 4>");
-}
-
-// MslPrinterTest.EmitType_ArrayOfArray
-TEST_F(MslASTPrinterTest, EmitType_ArrayOfArray) {
-    auto a = ty.array<bool, 4>();
-    auto b = ty.array(a, 5_u);
-    ast::Type type = GlobalVar("G", b, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type))) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "tint_array<tint_array<bool, 4>, 5>");
-}
-
-// MslPrinterTest.EmitType_ArrayOfArrayOfArray
-TEST_F(MslASTPrinterTest, EmitType_ArrayOfArrayOfArray) {
-    auto a = ty.array<bool, 4>();
-    auto b = ty.array(a, 5_u);
-    auto c = ty.array(b, 6_u);
-    ast::Type type = GlobalVar("G", c, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type))) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "tint_array<tint_array<tint_array<bool, 4>, 5>, 6>");
-}
-
-// TODO(dsinclair): Port? Not sure if this is relevant ...
-TEST_F(MslASTPrinterTest, EmitType_Array_WithoutName) {
-    auto arr = ty.array<bool, 4>();
-    ast::Type type = GlobalVar("G", arr, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type))) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "tint_array<bool, 4>");
-}
-
-// MslPrinterTest.EmitType_RuntimeArray
-TEST_F(MslASTPrinterTest, EmitType_RuntimeArray) {
-    auto arr = ty.array<bool, 1>();
-    ast::Type type = GlobalVar("G", arr, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type))) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "tint_array<bool, 1>");
-}
-
-// MSLPrinterTest.EmitType_Bool
-TEST_F(MslASTPrinterTest, EmitType_Bool) {
-    auto* bool_ = create<core::type::Bool>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, bool_)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "bool");
-}
-
-// MSLPrintertest.EmitType_F32
-TEST_F(MslASTPrinterTest, EmitType_F32) {
-    auto* f32 = create<core::type::F32>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, f32)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float");
-}
-
-// MSLPrinterTest.EmitType_F16
-TEST_F(MslASTPrinterTest, EmitType_F16) {
-    auto* f16 = create<core::type::F16>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, f16)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "half");
-}
-
-// MSLPrinterTest.EmitType_I32
-TEST_F(MslASTPrinterTest, EmitType_I32) {
-    auto* i32 = create<core::type::I32>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, i32)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "int");
-}
-
-// MSLPrinterTest.EmitType_Matrix_F32
-TEST_F(MslASTPrinterTest, EmitType_Matrix_F32) {
-    auto* f32 = create<core::type::F32>();
-    auto* vec3 = create<core::type::Vector>(f32, 3u);
-    auto* mat2x3 = create<core::type::Matrix>(vec3, 2u);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, mat2x3)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float2x3");
-}
-
-// MSLPrinterTest.EmitType_Matrix_F16
-TEST_F(MslASTPrinterTest, EmitType_Matrix_F16) {
-    auto* f16 = create<core::type::F16>();
-    auto* vec3 = create<core::type::Vector>(f16, 3u);
-    auto* mat2x3 = create<core::type::Matrix>(vec3, 2u);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, mat2x3)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "half2x3");
-}
-
-// TODO(dsinclair): Not sure if this is relevant? MslPrinterTest.EmitType_Pointer_Workgroup
-TEST_F(MslASTPrinterTest, EmitType_Pointer) {
-    auto* f32 = create<core::type::F32>();
-    auto* p =
-        create<core::type::Pointer>(core::AddressSpace::kWorkgroup, f32, core::Access::kReadWrite);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, p)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "threadgroup float*");
-}
-
-TEST_F(MslASTPrinterTest, EmitType_Struct) {
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", ty.f32()),
-                             });
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(s))) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "S");
-}
-
-// MSLPrinterTest.EmitType_Struct
-TEST_F(MslASTPrinterTest, EmitType_StructDecl) {
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", ty.f32()),
-                             });
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(s)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-    EXPECT_EQ(buf.String(), R"(struct S {
-  int a;
-  float b;
-};
-)");
-}
-
-// MSLPrinterTest.EmitType_Struct_Layout_NonComposites
-TEST_F(MslASTPrinterTest, EmitType_Struct_Layout_NonComposites) {
-    auto* s =
-        Structure("S", Vector{
-                           Member("a", ty.i32(), Vector{MemberSize(32_a)}),
-                           Member("b", ty.f32(), Vector{MemberAlign(128_i), MemberSize(128_a)}),
-                           Member("c", ty.vec2<f32>()),
-                           Member("d", ty.u32()),
-                           Member("e", ty.vec3<f32>()),
-                           Member("f", ty.u32()),
-                           Member("g", ty.vec4<f32>()),
-                           Member("h", ty.u32()),
-                           Member("i", ty.mat2x2<f32>()),
-                           Member("j", ty.u32()),
-                           Member("k", ty.mat2x3<f32>()),
-                           Member("l", ty.u32()),
-                           Member("m", ty.mat2x4<f32>()),
-                           Member("n", ty.u32()),
-                           Member("o", ty.mat3x2<f32>()),
-                           Member("p", ty.u32()),
-                           Member("q", ty.mat3x3<f32>()),
-                           Member("r", ty.u32()),
-                           Member("s", ty.mat3x4<f32>()),
-                           Member("t", ty.u32()),
-                           Member("u", ty.mat4x2<f32>()),
-                           Member("v", ty.u32()),
-                           Member("w", ty.mat4x3<f32>()),
-                           Member("x", ty.u32()),
-                           Member("y", ty.mat4x4<f32>()),
-                           Member("z", ty.f32()),
-                       });
-
-    ast::Type type = GlobalVar("G", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead,
-                               Binding(0_a), Group(0_a))
-                         ->type;
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(type)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
-    // for each field of the structure s.
-#define ALL_FIELDS()                       \
-    FIELD(0x0000, int, 0, a)               \
-    FIELD(0x0004, int8_t, 124, tint_pad)   \
-    FIELD(0x0080, float, 0, b)             \
-    FIELD(0x0084, int8_t, 124, tint_pad_1) \
-    FIELD(0x0100, float2, 0, c)            \
-    FIELD(0x0108, uint, 0, d)              \
-    FIELD(0x010c, int8_t, 4, tint_pad_2)   \
-    FIELD(0x0110, float3, 0, e)            \
-    FIELD(0x011c, uint, 0, f)              \
-    FIELD(0x0120, float4, 0, g)            \
-    FIELD(0x0130, uint, 0, h)              \
-    FIELD(0x0134, int8_t, 4, tint_pad_3)   \
-    FIELD(0x0138, float2x2, 0, i)          \
-    FIELD(0x0148, uint, 0, j)              \
-    FIELD(0x014c, int8_t, 4, tint_pad_4)   \
-    FIELD(0x0150, float2x3, 0, k)          \
-    FIELD(0x0170, uint, 0, l)              \
-    FIELD(0x0174, int8_t, 12, tint_pad_5)  \
-    FIELD(0x0180, float2x4, 0, m)          \
-    FIELD(0x01a0, uint, 0, n)              \
-    FIELD(0x01a4, int8_t, 4, tint_pad_6)   \
-    FIELD(0x01a8, float3x2, 0, o)          \
-    FIELD(0x01c0, uint, 0, p)              \
-    FIELD(0x01c4, int8_t, 12, tint_pad_7)  \
-    FIELD(0x01d0, float3x3, 0, q)          \
-    FIELD(0x0200, uint, 0, r)              \
-    FIELD(0x0204, int8_t, 12, tint_pad_8)  \
-    FIELD(0x0210, float3x4, 0, s)          \
-    FIELD(0x0240, uint, 0, t)              \
-    FIELD(0x0244, int8_t, 4, tint_pad_9)   \
-    FIELD(0x0248, float4x2, 0, u)          \
-    FIELD(0x0268, uint, 0, v)              \
-    FIELD(0x026c, int8_t, 4, tint_pad_10)  \
-    FIELD(0x0270, float4x3, 0, w)          \
-    FIELD(0x02b0, uint, 0, x)              \
-    FIELD(0x02b4, int8_t, 12, tint_pad_11) \
-    FIELD(0x02c0, float4x4, 0, y)          \
-    FIELD(0x0300, float, 0, z)             \
-    FIELD(0x0304, int8_t, 124, tint_pad_12)
-
-    // Check that the generated string is as expected.
-    StringStream expect;
-    expect << "struct S {\n";
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
-    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
-    ALL_FIELDS()
-#undef FIELD
-    expect << "};\n";
-    EXPECT_EQ(buf.String(), expect.str());
-
-    // 1.4 Metal and C++14
-    // The Metal programming language is a C++14-based Specification with
-    // extensions and restrictions. Refer to the C++14 Specification (also known
-    // as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed
-    // description of the language grammar.
-    //
-    // Tint is written in C++14, so use the compiler to verify the generated
-    // layout is as expected for C++14 / MSL.
-    {
-        struct S {
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
-            ALL_FIELDS()
-#undef FIELD
-        };
-
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
-    EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
-        ALL_FIELDS()
-#undef FIELD
-    }
-#undef ALL_FIELDS
-}
-
-// MSLPrinterTest.EmitType_Struct_Layout_Structures
-TEST_F(MslASTPrinterTest, EmitType_Struct_Layout_Structures) {
-    // inner_x: size(1024), align(512)
-    auto* inner_x = Structure("inner_x", Vector{
-                                             Member("a", ty.i32()),
-                                             Member("b", ty.f32(), Vector{MemberAlign(512_i)}),
-                                         });
-
-    // inner_y: size(516), align(4)
-    auto* inner_y = Structure("inner_y", Vector{
-                                             Member("a", ty.i32(), Vector{MemberSize(512_a)}),
-                                             Member("b", ty.f32()),
-                                         });
-
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", ty.Of(inner_x)),
-                                 Member("c", ty.f32()),
-                                 Member("d", ty.Of(inner_y)),
-                                 Member("e", ty.f32()),
-                             });
-
-    ast::Type type = GlobalVar("G", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead,
-                               Binding(0_a), Group(0_a))
-                         ->type;
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(type)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
-    // for each field of the structure s.
-#define ALL_FIELDS()                     \
-    FIELD(0x0000, int, 0, a)             \
-    FIELD(0x0004, int8_t, 508, tint_pad) \
-    FIELD(0x0200, inner_x, 0, b)         \
-    FIELD(0x0600, float, 0, c)           \
-    FIELD(0x0604, inner_y, 0, d)         \
-    FIELD(0x0808, float, 0, e)           \
-    FIELD(0x080c, int8_t, 500, tint_pad_4)
-
-    // Check that the generated string is as expected.
-    StringStream expect;
-    expect << "struct S {\n";
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
-    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
-    ALL_FIELDS()
-#undef FIELD
-    expect << "};\n";
-    EXPECT_EQ(buf.String(), expect.str());
-
-    // 1.4 Metal and C++14
-    // The Metal programming language is a C++14-based Specification with
-    // extensions and restrictions. Refer to the C++14 Specification (also known
-    // as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed
-    // description of the language grammar.
-    //
-    // Tint is written in C++14, so use the compiler to verify the generated
-    // layout is as expected for C++14 / MSL.
-    {
-        struct inner_x {
-            uint32_t a;
-            alignas(512) float b;
-        };
-        CHECK_TYPE_SIZE_AND_ALIGN(inner_x, 1024, 512);
-
-        struct inner_y {
-            uint32_t a[128];
-            float b;
-        };
-        CHECK_TYPE_SIZE_AND_ALIGN(inner_y, 516, 4);
-
-        struct S {
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
-            ALL_FIELDS()
-#undef FIELD
-        };
-
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
-    EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
-        ALL_FIELDS()
-#undef FIELD
-    }
-
-#undef ALL_FIELDS
-}
-
-// MSLPrinterTest.EmitType_Struct_Layout_ArrayDefaultStride
-TEST_F(MslASTPrinterTest, EmitType_Struct_Layout_ArrayDefaultStride) {
-    // inner: size(1024), align(512)
-    auto* inner = Structure("inner", Vector{
-                                         Member("a", ty.i32()),
-                                         Member("b", ty.f32(), Vector{MemberAlign(512_i)}),
-                                     });
-
-    // array_x: size(28), align(4)
-    auto array_x = ty.array<f32, 7>();
-
-    // array_y: size(4096), align(512)
-    auto array_y = ty.array(ty.Of(inner), 4_u);
-
-    // array_z: size(4), align(4)
-    auto array_z = ty.array<f32>();
-
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", array_x),
-                                 Member("c", ty.f32()),
-                                 Member("d", array_y),
-                                 Member("e", ty.f32()),
-                                 Member("f", array_z),
-                             });
-
-    ast::Type type = GlobalVar("G", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead,
-                               Binding(0_a), Group(0_a))
-                         ->type;
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(type)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
-    // for each field of the structure s.
-#define ALL_FIELDS()                     \
-    FIELD(0x0000, int, 0, a)             \
-    FIELD(0x0004, float, 7, b)           \
-    FIELD(0x0020, float, 0, c)           \
-    FIELD(0x0024, int8_t, 476, tint_pad) \
-    FIELD(0x0200, inner, 4, d)           \
-    FIELD(0x1200, float, 0, e)           \
-    FIELD(0x1204, float, 1, f)           \
-    FIELD(0x1208, int8_t, 504, tint_pad_3)
-
-    // Check that the generated string is as expected.
-    StringStream expect;
-    expect << "struct S {\n";
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
-    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
-    ALL_FIELDS()
-#undef FIELD
-    expect << "};\n";
-    EXPECT_EQ(buf.String(), expect.str());
-
-    // 1.4 Metal and C++14
-    // The Metal programming language is a C++14-based Specification with
-    // extensions and restrictions. Refer to the C++14 Specification (also known
-    // as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed
-    // description of the language grammar.
-    //
-    // Tint is written in C++14, so use the compiler to verify the generated
-    // layout is as expected for C++14 / MSL.
-    {
-        struct inner {
-            uint32_t a;
-            alignas(512) float b;
-        };
-        CHECK_TYPE_SIZE_AND_ALIGN(inner, 1024, 512);
-
-        // array_x: size(28), align(4)
-        using array_x = std::array<float, 7>;
-        CHECK_TYPE_SIZE_AND_ALIGN(array_x, 28, 4);
-
-        // array_y: size(4096), align(512)
-        using array_y = std::array<inner, 4>;
-        CHECK_TYPE_SIZE_AND_ALIGN(array_y, 4096, 512);
-
-        // array_z: size(4), align(4)
-        using array_z = std::array<float, 1>;
-        CHECK_TYPE_SIZE_AND_ALIGN(array_z, 4, 4);
-
-        struct S {
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
-            ALL_FIELDS()
-#undef FIELD
-        };
-
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
-    EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
-        ALL_FIELDS()
-#undef FIELD
-    }
-
-#undef ALL_FIELDS
-}
-
-// MslPrinterTest.EmitType_Struct_Layout_ArrayVec3DefaultStride
-TEST_F(MslASTPrinterTest, EmitType_Struct_Layout_ArrayVec3DefaultStride) {
-    // array: size(64), align(16)
-    auto array = ty.array<vec3<f32>, 4>();
-
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", array),
-                                 Member("c", ty.i32()),
-                             });
-
-    ast::Type type = GlobalVar("G", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead,
-                               Binding(0_a), Group(0_a))
-                         ->type;
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(type)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
-    // for each field of the structure s.
-#define ALL_FIELDS()                    \
-    FIELD(0x0000, int, 0, a)            \
-    FIELD(0x0004, int8_t, 12, tint_pad) \
-    FIELD(0x0010, float3, 4, b)         \
-    FIELD(0x0050, int, 0, c)            \
-    FIELD(0x0054, int8_t, 12, tint_pad_1)
-
-    // Check that the generated string is as expected.
-    StringStream expect;
-    expect << "struct S {\n";
-#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
-    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
-    ALL_FIELDS()
-#undef FIELD
-    expect << "};\n";
-    EXPECT_EQ(buf.String(), expect.str());
-}
-
-// MslPrinterTest.AttemptTintPadSymbolCollision
-TEST_F(MslASTPrinterTest, AttemptTintPadSymbolCollision) {
-    auto* s = Structure(
-        "S", Vector{
-                 // uses symbols tint_pad_[0..9] and tint_pad_[20..35]
-                 Member("tint_pad_2", ty.i32(), Vector{MemberSize(32_a)}),
-                 Member("tint_pad_20", ty.f32(), Vector{MemberAlign(128_i), MemberSize(128_u)}),
-                 Member("tint_pad_33", ty.vec2<f32>()),
-                 Member("tint_pad_1", ty.u32()),
-                 Member("tint_pad_3", ty.vec3<f32>()),
-                 Member("tint_pad_7", ty.u32()),
-                 Member("tint_pad_25", ty.vec4<f32>()),
-                 Member("tint_pad_5", ty.u32()),
-                 Member("tint_pad_27", ty.mat2x2<f32>()),
-                 Member("tint_pad_24", ty.u32()),
-                 Member("tint_pad_23", ty.mat2x3<f32>()),
-                 Member("tint_pad", ty.u32()),
-                 Member("tint_pad_8", ty.mat2x4<f32>()),
-                 Member("tint_pad_26", ty.u32()),
-                 Member("tint_pad_29", ty.mat3x2<f32>()),
-                 Member("tint_pad_6", ty.u32()),
-                 Member("tint_pad_22", ty.mat3x3<f32>()),
-                 Member("tint_pad_32", ty.u32()),
-                 Member("tint_pad_34", ty.mat3x4<f32>()),
-                 Member("tint_pad_35", ty.u32()),
-                 Member("tint_pad_30", ty.mat4x2<f32>()),
-                 Member("tint_pad_9", ty.u32()),
-                 Member("tint_pad_31", ty.mat4x3<f32>()),
-                 Member("tint_pad_28", ty.u32()),
-                 Member("tint_pad_4", ty.mat4x4<f32>()),
-                 Member("tint_pad_21", ty.f32()),
-             });
-
-    ast::Type type = GlobalVar("G", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead,
-                               Binding(0_a), Group(0_a))
-                         ->type;
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(type)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-    EXPECT_EQ(buf.String(), R"(struct S {
-  /* 0x0000 */ int tint_pad_2;
-  /* 0x0004 */ tint_array<int8_t, 124> tint_pad_10;
-  /* 0x0080 */ float tint_pad_20;
-  /* 0x0084 */ tint_array<int8_t, 124> tint_pad_11;
-  /* 0x0100 */ float2 tint_pad_33;
-  /* 0x0108 */ uint tint_pad_1;
-  /* 0x010c */ tint_array<int8_t, 4> tint_pad_12;
-  /* 0x0110 */ float3 tint_pad_3;
-  /* 0x011c */ uint tint_pad_7;
-  /* 0x0120 */ float4 tint_pad_25;
-  /* 0x0130 */ uint tint_pad_5;
-  /* 0x0134 */ tint_array<int8_t, 4> tint_pad_13;
-  /* 0x0138 */ float2x2 tint_pad_27;
-  /* 0x0148 */ uint tint_pad_24;
-  /* 0x014c */ tint_array<int8_t, 4> tint_pad_14;
-  /* 0x0150 */ float2x3 tint_pad_23;
-  /* 0x0170 */ uint tint_pad;
-  /* 0x0174 */ tint_array<int8_t, 12> tint_pad_15;
-  /* 0x0180 */ float2x4 tint_pad_8;
-  /* 0x01a0 */ uint tint_pad_26;
-  /* 0x01a4 */ tint_array<int8_t, 4> tint_pad_16;
-  /* 0x01a8 */ float3x2 tint_pad_29;
-  /* 0x01c0 */ uint tint_pad_6;
-  /* 0x01c4 */ tint_array<int8_t, 12> tint_pad_17;
-  /* 0x01d0 */ float3x3 tint_pad_22;
-  /* 0x0200 */ uint tint_pad_32;
-  /* 0x0204 */ tint_array<int8_t, 12> tint_pad_18;
-  /* 0x0210 */ float3x4 tint_pad_34;
-  /* 0x0240 */ uint tint_pad_35;
-  /* 0x0244 */ tint_array<int8_t, 4> tint_pad_19;
-  /* 0x0248 */ float4x2 tint_pad_30;
-  /* 0x0268 */ uint tint_pad_9;
-  /* 0x026c */ tint_array<int8_t, 4> tint_pad_36;
-  /* 0x0270 */ float4x3 tint_pad_31;
-  /* 0x02b0 */ uint tint_pad_28;
-  /* 0x02b4 */ tint_array<int8_t, 12> tint_pad_37;
-  /* 0x02c0 */ float4x4 tint_pad_4;
-  /* 0x0300 */ float tint_pad_21;
-  /* 0x0304 */ tint_array<int8_t, 124> tint_pad_38;
-};
-)");
-}
-
-// TODO(dsinclair): Missing from IR tests, but test also doesn't look right.
-TEST_F(MslASTPrinterTest, EmitType_Struct_WithAttribute) {
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", ty.f32()),
-                             });
-
-    ast::Type type = GlobalVar("G", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead,
-                               Binding(0_a), Group(0_a))
-                         ->type;
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(type)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-    EXPECT_EQ(buf.String(), R"(struct S {
-  /* 0x0000 */ int a;
-  /* 0x0004 */ float b;
-};
-)");
-}
-
-// MSLPrinterTest.EmitType_U32
-TEST_F(MslASTPrinterTest, EmitType_U32) {
-    auto* u32 = create<core::type::U32>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, u32)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "uint");
-}
-
-// MSLPrinterTest.EmitType_Vector
-TEST_F(MslASTPrinterTest, EmitType_Vector) {
-    auto* f32 = create<core::type::F32>();
-    auto* vec3 = create<core::type::Vector>(f32, 3u);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, vec3)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float3");
-}
-
-// MSLPrinterTest.EmitType_Void
-TEST_F(MslASTPrinterTest, EmitType_Void) {
-    auto* void_ = create<core::type::Void>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, void_)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "void");
-}
-
-// MSLPrinterTest.EmitType_Sampler
-TEST_F(MslASTPrinterTest, EmitType_Sampler) {
-    auto* sampler = create<core::type::Sampler>(core::type::SamplerKind::kSampler);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, sampler)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "sampler");
-}
-
-// MSLPrinterTest.EmitType_SamplerComparison
-TEST_F(MslASTPrinterTest, EmitType_SamplerComparison) {
-    auto* sampler = create<core::type::Sampler>(core::type::SamplerKind::kComparisonSampler);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, sampler)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "sampler");
-}
-
-struct MslDepthTextureData {
-    core::type::TextureDimension dim;
-    std::string result;
-};
-inline std::ostream& operator<<(std::ostream& out, MslDepthTextureData data) {
-    StringStream str;
-    str << data.dim;
-    out << str.str();
-    return out;
-}
-using MslDepthTexturesTest = TestParamHelper<MslDepthTextureData>;
-// MslPrinterDepthTexturesTest.Emit
-TEST_P(MslDepthTexturesTest, Emit) {
-    auto params = GetParam();
-
-    core::type::DepthTexture s(params.dim);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, &s)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-INSTANTIATE_TEST_SUITE_P(
-    MslASTPrinterTest,
-    MslDepthTexturesTest,
-    testing::Values(MslDepthTextureData{core::type::TextureDimension::k2d,
-                                        "depth2d<float, access::sample>"},
-                    MslDepthTextureData{core::type::TextureDimension::k2dArray,
-                                        "depth2d_array<float, access::sample>"},
-                    MslDepthTextureData{core::type::TextureDimension::kCube,
-                                        "depthcube<float, access::sample>"},
-                    MslDepthTextureData{core::type::TextureDimension::kCubeArray,
-                                        "depthcube_array<float, access::sample>"}));
-
-using MslDepthMultisampledTexturesTest = TestHelper;
-// MSLPrinterTest.EmitType_DepthMultisampledTexture
-TEST_F(MslDepthMultisampledTexturesTest, Emit) {
-    core::type::DepthMultisampledTexture s(core::type::TextureDimension::k2d);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, &s)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "depth2d_ms<float, access::read>");
-}
-
-struct MslTextureData {
-    core::type::TextureDimension dim;
-    std::string result;
-};
-inline std::ostream& operator<<(std::ostream& out, MslTextureData data) {
-    StringStream str;
-    str << data.dim;
-    out << str.str();
-    return out;
-}
-using MslSampledtexturesTest = TestParamHelper<MslTextureData>;
-// MslPrinterSampledTexturesTest.Emit
-TEST_P(MslSampledtexturesTest, Emit) {
-    auto params = GetParam();
-
-    auto* f32 = create<core::type::F32>();
-    auto* s = create<core::type::SampledTexture>(params.dim, f32);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, s)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-INSTANTIATE_TEST_SUITE_P(
-    MslASTPrinterTest,
-    MslSampledtexturesTest,
-    testing::Values(
-        MslTextureData{core::type::TextureDimension::k1d, "texture1d<float, access::sample>"},
-        MslTextureData{core::type::TextureDimension::k2d, "texture2d<float, access::sample>"},
-        MslTextureData{core::type::TextureDimension::k2dArray,
-                       "texture2d_array<float, access::sample>"},
-        MslTextureData{core::type::TextureDimension::k3d, "texture3d<float, access::sample>"},
-        MslTextureData{core::type::TextureDimension::kCube, "texturecube<float, access::sample>"},
-        MslTextureData{core::type::TextureDimension::kCubeArray,
-                       "texturecube_array<float, access::sample>"}));
-
-// MslPrinterTest.Emit_TypeMultisampledTexture
-TEST_F(MslASTPrinterTest, Emit_TypeMultisampledTexture) {
-    auto* u32 = create<core::type::U32>();
-    auto* ms = create<core::type::MultisampledTexture>(core::type::TextureDimension::k2d, u32);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, ms)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "texture2d_ms<uint, access::read>");
-}
-
-struct MslStorageTextureData {
-    core::type::TextureDimension dim;
-    std::string result;
-};
-inline std::ostream& operator<<(std::ostream& out, MslStorageTextureData data) {
-    StringStream str;
-    str << data.dim;
-    return out << str.str();
-}
-using MslStorageTexturesTest = TestParamHelper<MslStorageTextureData>;
-// MslPrinterStorageTexturesTest.Emit
-TEST_P(MslStorageTexturesTest, Emit) {
-    auto params = GetParam();
-
-    auto s = ty.storage_texture(params.dim, core::TexelFormat::kR32Float, core::Access::kWrite);
-    ast::Type type = GlobalVar("test_var", s, Binding(0_a), Group(0_a))->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(type))) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslStorageTexturesTest,
-                         testing::Values(MslStorageTextureData{core::type::TextureDimension::k1d,
-                                                               "texture1d<float, access::write>"},
-                                         MslStorageTextureData{core::type::TextureDimension::k2d,
-                                                               "texture2d<float, access::write>"},
-                                         MslStorageTextureData{
-                                             core::type::TextureDimension::k2dArray,
-                                             "texture2d_array<float, access::write>"},
-                                         MslStorageTextureData{core::type::TextureDimension::k3d,
-                                                               "texture3d<float, access::write>"}));
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/binary_test.cc b/src/tint/lang/msl/writer/ast_printer/binary_test.cc
deleted file mode 100644
index 70601fc..0000000
--- a/src/tint/lang/msl/writer/ast_printer/binary_test.cc
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-// All ported to IR.
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-struct BinaryData {
-    const char* result;
-    core::BinaryOp op;
-};
-inline std::ostream& operator<<(std::ostream& out, BinaryData data) {
-    StringStream str;
-    str << data.op;
-    out << str.str();
-    return out;
-}
-
-// MSLPrinterBinaryTest.Emit
-using MslBinaryTest = TestParamHelper<BinaryData>;
-TEST_P(MslBinaryTest, Emit) {
-    auto params = GetParam();
-
-    auto type = [&] {
-        return ((params.op == core::BinaryOp::kLogicalAnd) ||
-                (params.op == core::BinaryOp::kLogicalOr))
-                   ? ty.bool_()
-                   : ty.u32();
-    };
-
-    auto* left = Var("left", type());
-    auto* right = Var("right", type());
-
-    auto* expr = create<ast::BinaryExpression>(params.op, Expr(left), Expr(right));
-    WrapInFunction(left, right, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-INSTANTIATE_TEST_SUITE_P(
-    MslASTPrinterTest,
-    MslBinaryTest,
-    testing::Values(BinaryData{"(left & right)", core::BinaryOp::kAnd},
-                    BinaryData{"(left | right)", core::BinaryOp::kOr},
-                    BinaryData{"(left ^ right)", core::BinaryOp::kXor},
-                    BinaryData{"(left && right)", core::BinaryOp::kLogicalAnd},
-                    BinaryData{"(left || right)", core::BinaryOp::kLogicalOr},
-                    BinaryData{"(left == right)", core::BinaryOp::kEqual},
-                    BinaryData{"(left != right)", core::BinaryOp::kNotEqual},
-                    BinaryData{"(left < right)", core::BinaryOp::kLessThan},
-                    BinaryData{"(left > right)", core::BinaryOp::kGreaterThan},
-                    BinaryData{"(left <= right)", core::BinaryOp::kLessThanEqual},
-                    BinaryData{"(left >= right)", core::BinaryOp::kGreaterThanEqual},
-                    BinaryData{"(left << right)", core::BinaryOp::kShiftLeft},
-                    BinaryData{"(left >> right)", core::BinaryOp::kShiftRight},
-                    BinaryData{"(left + right)", core::BinaryOp::kAdd},
-                    BinaryData{"(left - right)", core::BinaryOp::kSubtract},
-                    BinaryData{"(left * right)", core::BinaryOp::kMultiply},
-                    BinaryData{"(left / right)", core::BinaryOp::kDivide},
-                    BinaryData{"(left % right)", core::BinaryOp::kModulo}));
-
-// MSLPrinterBinaryTest_SignedOverflowDefinedBehaviour.Emit
-using MslBinaryTest_SignedOverflowDefinedBehaviour = TestParamHelper<BinaryData>;
-TEST_P(MslBinaryTest_SignedOverflowDefinedBehaviour, Emit) {
-    auto params = GetParam();
-
-    auto a_type = ty.i32();
-    auto b_type =
-        (params.op == core::BinaryOp::kShiftLeft || params.op == core::BinaryOp::kShiftRight)
-            ? ty.u32()
-            : ty.i32();
-
-    auto* a = Var("a", a_type);
-    auto* b = Var("b", b_type);
-
-    auto* expr = create<ast::BinaryExpression>(params.op, Expr(a), Expr(b));
-    WrapInFunction(a, b, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-using Op = core::BinaryOp;
-constexpr BinaryData signed_overflow_defined_behaviour_cases[] = {
-    {"as_type<int>((as_type<uint>(a) << b))", Op::kShiftLeft},
-    {"(a >> b)", Op::kShiftRight},
-    {"as_type<int>((as_type<uint>(a) + as_type<uint>(b)))", Op::kAdd},
-    {"as_type<int>((as_type<uint>(a) - as_type<uint>(b)))", Op::kSubtract},
-    {"as_type<int>((as_type<uint>(a) * as_type<uint>(b)))", Op::kMultiply}};
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslBinaryTest_SignedOverflowDefinedBehaviour,
-                         testing::ValuesIn(signed_overflow_defined_behaviour_cases));
-
-// MSLPrinterBinaryTest_ShiftSignedOverflowDefinedBehaviour_Chained.Emit
-using MslBinaryTest_SignedOverflowDefinedBehaviour_Chained = TestParamHelper<BinaryData>;
-TEST_P(MslBinaryTest_SignedOverflowDefinedBehaviour_Chained, Emit) {
-    auto params = GetParam();
-
-    auto a_type = ty.i32();
-    auto b_type =
-        (params.op == core::BinaryOp::kShiftLeft || params.op == core::BinaryOp::kShiftRight)
-            ? ty.u32()
-            : ty.i32();
-
-    auto* a = Var("a", a_type);
-    auto* b = Var("b", b_type);
-
-    auto* expr1 = create<ast::BinaryExpression>(params.op, Expr(a), Expr(b));
-    auto* expr2 = create<ast::BinaryExpression>(params.op, expr1, Expr(b));
-    WrapInFunction(a, b, expr2);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr2)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-using Op = core::BinaryOp;
-constexpr BinaryData signed_overflow_defined_behaviour_chained_cases[] = {
-    {R"(as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(a) << b))) << b)))",
-     Op::kShiftLeft},
-    {R"(((a >> b) >> b))", Op::kShiftRight},
-    {R"(as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(a) + as_type<uint>(b)))) + as_type<uint>(b))))",
-     Op::kAdd},
-    {R"(as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(a) - as_type<uint>(b)))) - as_type<uint>(b))))",
-     Op::kSubtract},
-    {R"(as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(a) * as_type<uint>(b)))) * as_type<uint>(b))))",
-     Op::kMultiply}};
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslBinaryTest_SignedOverflowDefinedBehaviour_Chained,
-                         testing::ValuesIn(signed_overflow_defined_behaviour_chained_cases));
-
-// MslPrinterTest.BinaryModF32
-TEST_F(MslBinaryTest, ModF32) {
-    auto* left = Var("left", ty.f32());
-    auto* right = Var("right", ty.f32());
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(left), Expr(right));
-    WrapInFunction(left, right, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "fmod(left, right)");
-}
-
-// MslPrinterTest.BinaryModF16
-TEST_F(MslBinaryTest, ModF16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* left = Var("left", ty.f16());
-    auto* right = Var("right", ty.f16());
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(left), Expr(right));
-    WrapInFunction(left, right, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "fmod(left, right)");
-}
-
-// MslBinaryTest.BinaryModVecF32
-TEST_F(MslBinaryTest, ModVec3F32) {
-    auto* left = Var("left", ty.vec3<f32>());
-    auto* right = Var("right", ty.vec3<f32>());
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(left), Expr(right));
-    WrapInFunction(left, right, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "fmod(left, right)");
-}
-
-// MslPrinterTest.BinaryModVec3F16
-TEST_F(MslBinaryTest, ModVec3F16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* left = Var("left", ty.vec3<f16>());
-    auto* right = Var("right", ty.vec3<f16>());
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kModulo, Expr(left), Expr(right));
-    WrapInFunction(left, right, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "fmod(left, right)");
-}
-
-// MslPrinterTest.BinaryBoolAnd
-TEST_F(MslBinaryTest, BoolAnd) {
-    auto* left = Var("left", Expr(true));
-    auto* right = Var("right", Expr(false));
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kAnd, Expr(left), Expr(right));
-    WrapInFunction(left, right, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "bool(left & right)");
-}
-
-// MslPrinterTest.BinaryBoolOr
-TEST_F(MslBinaryTest, BoolOr) {
-    auto* left = Var("left", Expr(true));
-    auto* right = Var("right", Expr(false));
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kOr, Expr(left), Expr(right));
-    WrapInFunction(left, right, expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "bool(left | right)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/bitcast_test.cc b/src/tint/lang/msl/writer/ast_printer/bitcast_test.cc
deleted file mode 100644
index 351da46..0000000
--- a/src/tint/lang/msl/writer/ast_printer/bitcast_test.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, EmitExpression_Bitcast) {
-    auto* a = Let("a", Expr(1_i));
-    auto* bitcast = Bitcast<f32>(Expr("a"));
-    WrapInFunction(a, bitcast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "as_type<float>(a)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/block_test.cc b/src/tint/lang/msl/writer/ast_printer/block_test.cc
deleted file mode 100644
index 41d43c7..0000000
--- a/src/tint/lang/msl/writer/ast_printer/block_test.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_Block) {
-    auto* b = Block(Return());
-    WrapInFunction(b);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    return;
-  }
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Block_WithoutNewline) {
-    auto* b = Block(Return());
-    WrapInFunction(b);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitBlock(b)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    return;
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/break_test.cc b/src/tint/lang/msl/writer/ast_printer/break_test.cc
deleted file mode 100644
index a71988a..0000000
--- a/src/tint/lang/msl/writer/ast_printer/break_test.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_Break) {
-    auto* b = create<ast::BreakStatement>();
-    WrapInFunction(Loop(Block(b)));
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  break;\n");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/builtin_test.cc b/src/tint/lang/msl/writer/ast_printer/builtin_test.cc
deleted file mode 100644
index fff3db0..0000000
--- a/src/tint/lang/msl/writer/ast_printer/builtin_test.cc
+++ /dev/null
@@ -1,1206 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using MslASTPrinterTest = TestHelper;
-
-enum class CallParamType {
-    kF32,
-    kU32,
-    kBool,
-    kF16,
-};
-
-struct BuiltinData {
-    wgsl::BuiltinFn builtin;
-    CallParamType type;
-    const char* msl_name;
-};
-inline std::ostream& operator<<(std::ostream& out, BuiltinData data) {
-    out << data.msl_name << "<";
-    switch (data.type) {
-        case CallParamType::kF32:
-            out << "f32";
-            break;
-        case CallParamType::kU32:
-            out << "u32";
-            break;
-        case CallParamType::kBool:
-            out << "bool";
-            break;
-        case CallParamType::kF16:
-            out << "f16";
-            break;
-    }
-    out << ">";
-    return out;
-}
-
-const ast::CallExpression* GenerateCall(wgsl::BuiltinFn builtin,
-                                        CallParamType type,
-                                        ProgramBuilder* builder) {
-    std::string name;
-    StringStream str;
-    str << name << builtin;
-    switch (builtin) {
-        case wgsl::BuiltinFn::kAcos:
-        case wgsl::BuiltinFn::kAsin:
-        case wgsl::BuiltinFn::kAtan:
-        case wgsl::BuiltinFn::kCeil:
-        case wgsl::BuiltinFn::kCos:
-        case wgsl::BuiltinFn::kCosh:
-        case wgsl::BuiltinFn::kDpdx:
-        case wgsl::BuiltinFn::kDpdxCoarse:
-        case wgsl::BuiltinFn::kDpdxFine:
-        case wgsl::BuiltinFn::kDpdy:
-        case wgsl::BuiltinFn::kDpdyCoarse:
-        case wgsl::BuiltinFn::kDpdyFine:
-        case wgsl::BuiltinFn::kExp:
-        case wgsl::BuiltinFn::kExp2:
-        case wgsl::BuiltinFn::kFloor:
-        case wgsl::BuiltinFn::kFract:
-        case wgsl::BuiltinFn::kFwidth:
-        case wgsl::BuiltinFn::kFwidthCoarse:
-        case wgsl::BuiltinFn::kFwidthFine:
-        case wgsl::BuiltinFn::kInverseSqrt:
-        case wgsl::BuiltinFn::kLength:
-        case wgsl::BuiltinFn::kLog:
-        case wgsl::BuiltinFn::kLog2:
-        case wgsl::BuiltinFn::kNormalize:
-        case wgsl::BuiltinFn::kRound:
-        case wgsl::BuiltinFn::kSin:
-        case wgsl::BuiltinFn::kSinh:
-        case wgsl::BuiltinFn::kSqrt:
-        case wgsl::BuiltinFn::kTan:
-        case wgsl::BuiltinFn::kTanh:
-        case wgsl::BuiltinFn::kTrunc:
-        case wgsl::BuiltinFn::kSign:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2");
-            } else {
-                return builder->Call(str.str(), "f2");
-            }
-        case wgsl::BuiltinFn::kLdexp:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "i2");
-            } else {
-                return builder->Call(str.str(), "f2", "i2");
-            }
-        case wgsl::BuiltinFn::kAtan2:
-        case wgsl::BuiltinFn::kDot:
-        case wgsl::BuiltinFn::kDistance:
-        case wgsl::BuiltinFn::kPow:
-        case wgsl::BuiltinFn::kReflect:
-        case wgsl::BuiltinFn::kStep:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2");
-            } else {
-                return builder->Call(str.str(), "f2", "f2");
-            }
-        case wgsl::BuiltinFn::kStorageBarrier:
-            return builder->Call(str.str());
-        case wgsl::BuiltinFn::kCross:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h3", "h3");
-            } else {
-                return builder->Call(str.str(), "f3", "f3");
-            }
-        case wgsl::BuiltinFn::kFma:
-        case wgsl::BuiltinFn::kMix:
-        case wgsl::BuiltinFn::kFaceForward:
-        case wgsl::BuiltinFn::kSmoothstep:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2", "h2");
-            } else {
-                return builder->Call(str.str(), "f2", "f2", "f2");
-            }
-        case wgsl::BuiltinFn::kAll:
-        case wgsl::BuiltinFn::kAny:
-            return builder->Call(str.str(), "b2");
-        case wgsl::BuiltinFn::kAbs:
-            if (type == CallParamType::kF32) {
-                return builder->Call(str.str(), "f2");
-            } else if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2");
-            } else {
-                return builder->Call(str.str(), "u2");
-            }
-        case wgsl::BuiltinFn::kCountLeadingZeros:
-        case wgsl::BuiltinFn::kCountOneBits:
-        case wgsl::BuiltinFn::kCountTrailingZeros:
-        case wgsl::BuiltinFn::kReverseBits:
-            return builder->Call(str.str(), "u2");
-        case wgsl::BuiltinFn::kExtractBits:
-            return builder->Call(str.str(), "u2", "u1", "u1");
-        case wgsl::BuiltinFn::kInsertBits:
-            return builder->Call(str.str(), "u2", "u2", "u1", "u1");
-        case wgsl::BuiltinFn::kMax:
-        case wgsl::BuiltinFn::kMin:
-            if (type == CallParamType::kF32) {
-                return builder->Call(str.str(), "f2", "f2");
-            } else if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2");
-            } else {
-                return builder->Call(str.str(), "u2", "u2");
-            }
-        case wgsl::BuiltinFn::kClamp:
-            if (type == CallParamType::kF32) {
-                return builder->Call(str.str(), "f2", "f2", "f2");
-            } else if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2", "h2");
-            } else {
-                return builder->Call(str.str(), "u2", "u2", "u2");
-            }
-        case wgsl::BuiltinFn::kSelect:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2", "b2");
-            } else {
-                return builder->Call(str.str(), "f2", "f2", "b2");
-            }
-        case wgsl::BuiltinFn::kDeterminant:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "hm2x2");
-            } else {
-                return builder->Call(str.str(), "m2x2");
-            }
-        case wgsl::BuiltinFn::kPack2X16Snorm:
-        case wgsl::BuiltinFn::kPack2X16Unorm:
-            return builder->Call(str.str(), "f2");
-        case wgsl::BuiltinFn::kPack4X8Snorm:
-        case wgsl::BuiltinFn::kPack4X8Unorm:
-            return builder->Call(str.str(), "f4");
-        case wgsl::BuiltinFn::kUnpack4X8Snorm:
-        case wgsl::BuiltinFn::kUnpack4X8Unorm:
-        case wgsl::BuiltinFn::kUnpack2X16Snorm:
-        case wgsl::BuiltinFn::kUnpack2X16Unorm:
-            return builder->Call(str.str(), "u1");
-        case wgsl::BuiltinFn::kWorkgroupBarrier:
-            return builder->Call(str.str());
-        case wgsl::BuiltinFn::kTranspose:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "hm3x2");
-            } else {
-                return builder->Call(str.str(), "m3x2");
-            }
-        default:
-            break;
-    }
-    return nullptr;
-}
-
-using MslBuiltinTest = TestParamHelper<BuiltinData>;
-TEST_P(MslBuiltinTest, Emit) {
-    auto param = GetParam();
-
-    if (param.type == CallParamType::kF16) {
-        Enable(wgsl::Extension::kF16);
-
-        GlobalVar("h2", ty.vec2<f16>(), core::AddressSpace::kPrivate);
-        GlobalVar("h3", ty.vec3<f16>(), core::AddressSpace::kPrivate);
-        GlobalVar("hm2x2", ty.mat2x2<f16>(), core::AddressSpace::kPrivate);
-        GlobalVar("hm3x2", ty.mat3x2<f16>(), core::AddressSpace::kPrivate);
-    }
-
-    GlobalVar("f2", ty.vec2<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("f3", ty.vec3<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("f4", ty.vec4<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("u1", ty.u32(), core::AddressSpace::kPrivate);
-    GlobalVar("u2", ty.vec2<u32>(), core::AddressSpace::kPrivate);
-    GlobalVar("i2", ty.vec2<i32>(), core::AddressSpace::kPrivate);
-    GlobalVar("b2", ty.vec2<bool>(), core::AddressSpace::kPrivate);
-    GlobalVar("m2x2", ty.mat2x2<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("m3x2", ty.mat3x2<f32>(), core::AddressSpace::kPrivate);
-
-    auto* call = GenerateCall(param.builtin, param.type, this);
-    ASSERT_NE(nullptr, call) << "Unhandled builtin";
-    Func("func", tint::Empty, ty.void_(), Vector{Ignore(call)},
-         Vector{create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    auto* sem = program->Sem().Get<sem::Call>(call);
-    ASSERT_NE(sem, nullptr);
-    auto* target = sem->Target();
-    ASSERT_NE(target, nullptr);
-    auto* builtin = target->As<sem::BuiltinFn>();
-    ASSERT_NE(builtin, nullptr);
-
-    EXPECT_EQ(gen.generate_builtin_name(builtin), param.msl_name);
-}
-INSTANTIATE_TEST_SUITE_P(
-    MslASTPrinterTest,
-    MslBuiltinTest,
-    testing::Values(
-        /* Logical built-in */
-        BuiltinData{wgsl::BuiltinFn::kAll, CallParamType::kBool, "all"},
-        BuiltinData{wgsl::BuiltinFn::kAny, CallParamType::kBool, "any"},
-        BuiltinData{wgsl::BuiltinFn::kSelect, CallParamType::kF32, "select"},
-        /* Float built-in */
-        BuiltinData{wgsl::BuiltinFn::kAbs, CallParamType::kF32, "fabs"},
-        BuiltinData{wgsl::BuiltinFn::kAbs, CallParamType::kF16, "fabs"},
-        BuiltinData{wgsl::BuiltinFn::kAcos, CallParamType::kF32, "acos"},
-        BuiltinData{wgsl::BuiltinFn::kAcos, CallParamType::kF16, "acos"},
-        BuiltinData{wgsl::BuiltinFn::kAsin, CallParamType::kF32, "asin"},
-        BuiltinData{wgsl::BuiltinFn::kAsin, CallParamType::kF16, "asin"},
-        BuiltinData{wgsl::BuiltinFn::kAtan, CallParamType::kF32, "atan"},
-        BuiltinData{wgsl::BuiltinFn::kAtan, CallParamType::kF16, "atan"},
-        BuiltinData{wgsl::BuiltinFn::kAtan2, CallParamType::kF32, "atan2"},
-        BuiltinData{wgsl::BuiltinFn::kAtan2, CallParamType::kF16, "atan2"},
-        BuiltinData{wgsl::BuiltinFn::kCeil, CallParamType::kF32, "ceil"},
-        BuiltinData{wgsl::BuiltinFn::kCeil, CallParamType::kF16, "ceil"},
-        BuiltinData{wgsl::BuiltinFn::kClamp, CallParamType::kF32, "clamp"},
-        BuiltinData{wgsl::BuiltinFn::kClamp, CallParamType::kF16, "clamp"},
-        BuiltinData{wgsl::BuiltinFn::kCos, CallParamType::kF32, "cos"},
-        BuiltinData{wgsl::BuiltinFn::kCos, CallParamType::kF16, "cos"},
-        BuiltinData{wgsl::BuiltinFn::kCosh, CallParamType::kF32, "cosh"},
-        BuiltinData{wgsl::BuiltinFn::kCosh, CallParamType::kF16, "cosh"},
-        BuiltinData{wgsl::BuiltinFn::kCross, CallParamType::kF32, "cross"},
-        BuiltinData{wgsl::BuiltinFn::kCross, CallParamType::kF16, "cross"},
-        BuiltinData{wgsl::BuiltinFn::kDistance, CallParamType::kF32, "distance"},
-        BuiltinData{wgsl::BuiltinFn::kDistance, CallParamType::kF16, "distance"},
-        BuiltinData{wgsl::BuiltinFn::kExp, CallParamType::kF32, "exp"},
-        BuiltinData{wgsl::BuiltinFn::kExp, CallParamType::kF16, "exp"},
-        BuiltinData{wgsl::BuiltinFn::kExp2, CallParamType::kF32, "exp2"},
-        BuiltinData{wgsl::BuiltinFn::kExp2, CallParamType::kF16, "exp2"},
-        BuiltinData{wgsl::BuiltinFn::kFaceForward, CallParamType::kF32, "faceforward"},
-        BuiltinData{wgsl::BuiltinFn::kFaceForward, CallParamType::kF16, "faceforward"},
-        BuiltinData{wgsl::BuiltinFn::kFloor, CallParamType::kF32, "floor"},
-        BuiltinData{wgsl::BuiltinFn::kFloor, CallParamType::kF16, "floor"},
-        BuiltinData{wgsl::BuiltinFn::kFma, CallParamType::kF32, "fma"},
-        BuiltinData{wgsl::BuiltinFn::kFma, CallParamType::kF16, "fma"},
-        BuiltinData{wgsl::BuiltinFn::kFract, CallParamType::kF32, "fract"},
-        BuiltinData{wgsl::BuiltinFn::kFract, CallParamType::kF16, "fract"},
-        BuiltinData{wgsl::BuiltinFn::kInverseSqrt, CallParamType::kF32, "rsqrt"},
-        BuiltinData{wgsl::BuiltinFn::kInverseSqrt, CallParamType::kF16, "rsqrt"},
-        BuiltinData{wgsl::BuiltinFn::kLdexp, CallParamType::kF32, "ldexp"},
-        BuiltinData{wgsl::BuiltinFn::kLdexp, CallParamType::kF16, "ldexp"},
-        BuiltinData{wgsl::BuiltinFn::kLength, CallParamType::kF32, "length"},
-        BuiltinData{wgsl::BuiltinFn::kLength, CallParamType::kF16, "length"},
-        BuiltinData{wgsl::BuiltinFn::kLog, CallParamType::kF32, "log"},
-        BuiltinData{wgsl::BuiltinFn::kLog, CallParamType::kF16, "log"},
-        BuiltinData{wgsl::BuiltinFn::kLog2, CallParamType::kF32, "log2"},
-        BuiltinData{wgsl::BuiltinFn::kLog2, CallParamType::kF16, "log2"},
-        BuiltinData{wgsl::BuiltinFn::kMax, CallParamType::kF32, "fmax"},
-        BuiltinData{wgsl::BuiltinFn::kMax, CallParamType::kF16, "fmax"},
-        BuiltinData{wgsl::BuiltinFn::kMin, CallParamType::kF32, "fmin"},
-        BuiltinData{wgsl::BuiltinFn::kMin, CallParamType::kF16, "fmin"},
-        BuiltinData{wgsl::BuiltinFn::kNormalize, CallParamType::kF32, "normalize"},
-        BuiltinData{wgsl::BuiltinFn::kNormalize, CallParamType::kF16, "normalize"},
-        BuiltinData{wgsl::BuiltinFn::kPow, CallParamType::kF32, "powr"},
-        BuiltinData{wgsl::BuiltinFn::kPow, CallParamType::kF16, "powr"},
-        BuiltinData{wgsl::BuiltinFn::kReflect, CallParamType::kF32, "reflect"},
-        BuiltinData{wgsl::BuiltinFn::kReflect, CallParamType::kF16, "reflect"},
-        BuiltinData{wgsl::BuiltinFn::kSign, CallParamType::kF32, "sign"},
-        BuiltinData{wgsl::BuiltinFn::kSign, CallParamType::kF16, "sign"},
-        BuiltinData{wgsl::BuiltinFn::kSin, CallParamType::kF32, "sin"},
-        BuiltinData{wgsl::BuiltinFn::kSin, CallParamType::kF16, "sin"},
-        BuiltinData{wgsl::BuiltinFn::kSinh, CallParamType::kF32, "sinh"},
-        BuiltinData{wgsl::BuiltinFn::kSinh, CallParamType::kF16, "sinh"},
-        BuiltinData{wgsl::BuiltinFn::kSmoothstep, CallParamType::kF32, "smoothstep"},
-        BuiltinData{wgsl::BuiltinFn::kSmoothstep, CallParamType::kF16, "smoothstep"},
-        BuiltinData{wgsl::BuiltinFn::kSqrt, CallParamType::kF32, "sqrt"},
-        BuiltinData{wgsl::BuiltinFn::kSqrt, CallParamType::kF16, "sqrt"},
-        BuiltinData{wgsl::BuiltinFn::kStep, CallParamType::kF32, "step"},
-        BuiltinData{wgsl::BuiltinFn::kStep, CallParamType::kF16, "step"},
-        BuiltinData{wgsl::BuiltinFn::kTan, CallParamType::kF32, "tan"},
-        BuiltinData{wgsl::BuiltinFn::kTan, CallParamType::kF16, "tan"},
-        BuiltinData{wgsl::BuiltinFn::kTanh, CallParamType::kF32, "tanh"},
-        BuiltinData{wgsl::BuiltinFn::kTanh, CallParamType::kF16, "tanh"},
-        BuiltinData{wgsl::BuiltinFn::kTrunc, CallParamType::kF32, "trunc"},
-        BuiltinData{wgsl::BuiltinFn::kTrunc, CallParamType::kF16, "trunc"},
-        /* Integer built-in */
-        BuiltinData{wgsl::BuiltinFn::kAbs, CallParamType::kU32, "abs"},
-        BuiltinData{wgsl::BuiltinFn::kClamp, CallParamType::kU32, "clamp"},
-        BuiltinData{wgsl::BuiltinFn::kCountLeadingZeros, CallParamType::kU32, "clz"},
-        BuiltinData{wgsl::BuiltinFn::kCountOneBits, CallParamType::kU32, "popcount"},
-        BuiltinData{wgsl::BuiltinFn::kCountTrailingZeros, CallParamType::kU32, "ctz"},
-        BuiltinData{wgsl::BuiltinFn::kExtractBits, CallParamType::kU32, "extract_bits"},
-        BuiltinData{wgsl::BuiltinFn::kInsertBits, CallParamType::kU32, "insert_bits"},
-        BuiltinData{wgsl::BuiltinFn::kMax, CallParamType::kU32, "max"},
-        BuiltinData{wgsl::BuiltinFn::kMin, CallParamType::kU32, "min"},
-        BuiltinData{wgsl::BuiltinFn::kReverseBits, CallParamType::kU32, "reverse_bits"},
-        BuiltinData{wgsl::BuiltinFn::kRound, CallParamType::kU32, "rint"},
-        /* Matrix built-in */
-        BuiltinData{wgsl::BuiltinFn::kDeterminant, CallParamType::kF32, "determinant"},
-        BuiltinData{wgsl::BuiltinFn::kTranspose, CallParamType::kF32, "transpose"},
-        /* Vector built-in */
-        BuiltinData{wgsl::BuiltinFn::kDot, CallParamType::kF32, "dot"},
-        /* Derivate built-in */
-        BuiltinData{wgsl::BuiltinFn::kDpdx, CallParamType::kF32, "dfdx"},
-        BuiltinData{wgsl::BuiltinFn::kDpdxCoarse, CallParamType::kF32, "dfdx"},
-        BuiltinData{wgsl::BuiltinFn::kDpdxFine, CallParamType::kF32, "dfdx"},
-        BuiltinData{wgsl::BuiltinFn::kDpdy, CallParamType::kF32, "dfdy"},
-        BuiltinData{wgsl::BuiltinFn::kDpdyCoarse, CallParamType::kF32, "dfdy"},
-        BuiltinData{wgsl::BuiltinFn::kDpdyFine, CallParamType::kF32, "dfdy"},
-        BuiltinData{wgsl::BuiltinFn::kFwidth, CallParamType::kF32, "fwidth"},
-        BuiltinData{wgsl::BuiltinFn::kFwidthCoarse, CallParamType::kF32, "fwidth"},
-        BuiltinData{wgsl::BuiltinFn::kFwidthFine, CallParamType::kF32, "fwidth"},
-        /* Data packing builtin */
-        BuiltinData{wgsl::BuiltinFn::kPack4X8Snorm, CallParamType::kF32, "pack_float_to_snorm4x8"},
-        BuiltinData{wgsl::BuiltinFn::kPack4X8Unorm, CallParamType::kF32, "pack_float_to_unorm4x8"},
-        BuiltinData{wgsl::BuiltinFn::kPack2X16Snorm, CallParamType::kF32,
-                    "pack_float_to_snorm2x16"},
-        BuiltinData{wgsl::BuiltinFn::kPack2X16Unorm, CallParamType::kF32,
-                    "pack_float_to_unorm2x16"},
-        /* Data unpacking builtin */
-        BuiltinData{wgsl::BuiltinFn::kUnpack4X8Snorm, CallParamType::kU32,
-                    "unpack_snorm4x8_to_float"},
-        BuiltinData{wgsl::BuiltinFn::kUnpack4X8Unorm, CallParamType::kU32,
-                    "unpack_unorm4x8_to_float"},
-        BuiltinData{wgsl::BuiltinFn::kUnpack2X16Snorm, CallParamType::kU32,
-                    "unpack_snorm2x16_to_float"},
-        BuiltinData{wgsl::BuiltinFn::kUnpack2X16Unorm, CallParamType::kU32,
-                    "unpack_unorm2x16_to_float"}));
-
-TEST_F(MslASTPrinterTest, Builtin_Call) {
-    GlobalVar("param1", ty.vec2<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("param2", ty.vec2<f32>(), core::AddressSpace::kPrivate);
-
-    auto* call = Call("dot", "param1", "param2");
-    WrapInFunction(Decl(Var("r", call)));
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "dot(param1, param2)");
-}
-
-TEST_F(MslASTPrinterTest, StorageBarrier) {
-    auto* call = Call("storageBarrier");
-    WrapInFunction(CallStmt(call));
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "threadgroup_barrier(mem_flags::mem_device)");
-}
-
-TEST_F(MslASTPrinterTest, WorkgroupBarrier) {
-    auto* call = Call("workgroupBarrier");
-    WrapInFunction(CallStmt(call));
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "threadgroup_barrier(mem_flags::mem_threadgroup)");
-}
-
-TEST_F(MslASTPrinterTest, Runtime_Modf_Scalar_f32) {
-    WrapInFunction(Decl(Let("f", Expr(1.5_f))),  //
-                   Decl(Let("v", Call("modf", "f"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct modf_result_f32 {
-  float fract;
-  float whole;
-};
-modf_result_f32 tint_modf(float param_0) {
-  modf_result_f32 result;
-  result.fract = modf(param_0, result.whole);
-  return result;
-}
-
-kernel void test_function() {
-  float const f = 1.5f;
-  modf_result_f32 const v = tint_modf(f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Runtime_Modf_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("f", Expr(1.5_h))),  //
-                   Decl(Let("v", Call("modf", "f"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct modf_result_f16 {
-  half fract;
-  half whole;
-};
-modf_result_f16 tint_modf(half param_0) {
-  modf_result_f16 result;
-  result.fract = modf(param_0, result.whole);
-  return result;
-}
-
-kernel void test_function() {
-  half const f = 1.5h;
-  modf_result_f16 const v = tint_modf(f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Runtime_Modf_Vector_f32) {
-    WrapInFunction(Decl(Let("f", Call<vec3<f32>>(1.5_f, 2.5_f, 3.5_f))),  //
-                   Decl(Let("v", Call("modf", "f"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct modf_result_vec3_f32 {
-  float3 fract;
-  float3 whole;
-};
-modf_result_vec3_f32 tint_modf(float3 param_0) {
-  modf_result_vec3_f32 result;
-  result.fract = modf(param_0, result.whole);
-  return result;
-}
-
-kernel void test_function() {
-  float3 const f = float3(1.5f, 2.5f, 3.5f);
-  modf_result_vec3_f32 const v = tint_modf(f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Runtime_Modf_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("f", Call<vec3<f16>>(1.5_h, 2.5_h, 3.5_h))),  //
-                   Decl(Let("v", Call("modf", "f"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct modf_result_vec3_f16 {
-  half3 fract;
-  half3 whole;
-};
-modf_result_vec3_f16 tint_modf(half3 param_0) {
-  modf_result_vec3_f16 result;
-  result.fract = modf(param_0, result.whole);
-  return result;
-}
-
-kernel void test_function() {
-  half3 const f = half3(1.5h, 2.5h, 3.5h);
-  modf_result_vec3_f16 const v = tint_modf(f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Const_Modf_Scalar_f32) {
-    WrapInFunction(Decl(Let("v", Call("modf", 1.5_f))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct modf_result_f32 {
-  float fract;
-  float whole;
-};
-kernel void test_function() {
-  modf_result_f32 const v = modf_result_f32{.fract=0.5f, .whole=1.0f};
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Const_Modf_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("v", Call("modf", 1.5_h))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct modf_result_f16 {
-  half fract;
-  half whole;
-};
-kernel void test_function() {
-  modf_result_f16 const v = modf_result_f16{.fract=0.5h, .whole=1.0h};
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Const_Modf_Vector_f32) {
-    WrapInFunction(Decl(Let("v", Call("modf", Call<vec3<f32>>(1.5_f, 2.5_f, 3.5_f)))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct modf_result_vec3_f32 {
-  float3 fract;
-  float3 whole;
-};
-kernel void test_function() {
-  modf_result_vec3_f32 const v = modf_result_vec3_f32{.fract=float3(0.5f), .whole=float3(1.0f, 2.0f, 3.0f)};
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Const_Modf_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("v", Call("modf", Call<vec3<f16>>(1.5_h, 2.5_h, 3.5_h)))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct modf_result_vec3_f16 {
-  half3 fract;
-  half3 whole;
-};
-kernel void test_function() {
-  modf_result_vec3_f16 const v = modf_result_vec3_f16{.fract=half3(0.5h), .whole=half3(1.0h, 2.0h, 3.0h)};
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Runtime_Frexp_Scalar_f32) {
-    WrapInFunction(Var("f", Expr(1_f)),  //
-                   Var("v", Call("frexp", "f")));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct frexp_result_f32 {
-  float fract;
-  int exp;
-};
-frexp_result_f32 tint_frexp(float param_0) {
-  frexp_result_f32 result;
-  result.fract = frexp(param_0, result.exp);
-  return result;
-}
-
-kernel void test_function() {
-  float f = 1.0f;
-  frexp_result_f32 v = tint_frexp(f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Runtime_Frexp_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Var("f", Expr(1_h)),  //
-                   Var("v", Call("frexp", "f")));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct frexp_result_f16 {
-  half fract;
-  int exp;
-};
-frexp_result_f16 tint_frexp(half param_0) {
-  frexp_result_f16 result;
-  result.fract = frexp(param_0, result.exp);
-  return result;
-}
-
-kernel void test_function() {
-  half f = 1.0h;
-  frexp_result_f16 v = tint_frexp(f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Runtime_Frexp_Vector_f32) {
-    WrapInFunction(Var("f", Call<vec3<f32>>()),  //
-                   Var("v", Call("frexp", "f")));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct frexp_result_vec3_f32 {
-  float3 fract;
-  int3 exp;
-};
-frexp_result_vec3_f32 tint_frexp(float3 param_0) {
-  frexp_result_vec3_f32 result;
-  result.fract = frexp(param_0, result.exp);
-  return result;
-}
-
-kernel void test_function() {
-  float3 f = float3(0.0f);
-  frexp_result_vec3_f32 v = tint_frexp(f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Runtime_Frexp_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Var("f", Call<vec3<f16>>()),  //
-                   Var("v", Call("frexp", "f")));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct frexp_result_vec3_f16 {
-  half3 fract;
-  int3 exp;
-};
-frexp_result_vec3_f16 tint_frexp(half3 param_0) {
-  frexp_result_vec3_f16 result;
-  result.fract = frexp(param_0, result.exp);
-  return result;
-}
-
-kernel void test_function() {
-  half3 f = half3(0.0h);
-  frexp_result_vec3_f16 v = tint_frexp(f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Const_Frexp_Scalar_f32) {
-    WrapInFunction(Decl(Let("v", Call("frexp", 1_f))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct frexp_result_f32 {
-  float fract;
-  int exp;
-};
-kernel void test_function() {
-  frexp_result_f32 const v = frexp_result_f32{.fract=0.5f, .exp=1};
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Const_Frexp_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("v", Call("frexp", 1_h))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct frexp_result_f16 {
-  half fract;
-  int exp;
-};
-kernel void test_function() {
-  frexp_result_f16 const v = frexp_result_f16{.fract=0.5h, .exp=1};
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Const_Frexp_Vector_f32) {
-    WrapInFunction(Decl(Let("v", Call("frexp", Call<vec3<f32>>()))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct frexp_result_vec3_f32 {
-  float3 fract;
-  int3 exp;
-};
-kernel void test_function() {
-  frexp_result_vec3_f32 const v = frexp_result_vec3_f32{};
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Const_Frexp_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("v", Call("frexp", Call<vec3<f16>>()))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-struct frexp_result_vec3_f16 {
-  half3 fract;
-  int3 exp;
-};
-kernel void test_function() {
-  frexp_result_vec3_f16 const v = frexp_result_vec3_f16{};
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Degrees_Scalar_f32) {
-    auto* val = Var("val", ty.f32());
-    auto* call = Call("degrees", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-float tint_degrees(float param_0) {
-  return param_0 * 57.29577951308232286465;
-}
-
-kernel void test_function() {
-  float val = 0.0f;
-  float const tint_symbol = tint_degrees(val);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Degrees_Vector_f32) {
-    auto* val = Var("val", ty.vec3<f32>());
-    auto* call = Call("degrees", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-float3 tint_degrees(float3 param_0) {
-  return param_0 * 57.29577951308232286465;
-}
-
-kernel void test_function() {
-  float3 val = 0.0f;
-  float3 const tint_symbol = tint_degrees(val);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Degrees_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.f16());
-    auto* call = Call("degrees", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-half tint_degrees(half param_0) {
-  return param_0 * 57.29577951308232286465;
-}
-
-kernel void test_function() {
-  half val = 0.0h;
-  half const tint_symbol = tint_degrees(val);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Degrees_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.vec3<f16>());
-    auto* call = Call("degrees", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-half3 tint_degrees(half3 param_0) {
-  return param_0 * 57.29577951308232286465;
-}
-
-kernel void test_function() {
-  half3 val = 0.0h;
-  half3 const tint_symbol = tint_degrees(val);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Radians_Scalar_f32) {
-    auto* val = Var("val", ty.f32());
-    auto* call = Call("radians", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-float tint_radians(float param_0) {
-  return param_0 * 0.01745329251994329547;
-}
-
-kernel void test_function() {
-  float val = 0.0f;
-  float const tint_symbol = tint_radians(val);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Radians_Vector_f32) {
-    auto* val = Var("val", ty.vec3<f32>());
-    auto* call = Call("radians", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-float3 tint_radians(float3 param_0) {
-  return param_0 * 0.01745329251994329547;
-}
-
-kernel void test_function() {
-  float3 val = 0.0f;
-  float3 const tint_symbol = tint_radians(val);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Radians_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.f16());
-    auto* call = Call("radians", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-half tint_radians(half param_0) {
-  return param_0 * 0.01745329251994329547;
-}
-
-kernel void test_function() {
-  half val = 0.0h;
-  half const tint_symbol = tint_radians(val);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Radians_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.vec3<f16>());
-    auto* call = Call("radians", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-half3 tint_radians(half3 param_0) {
-  return param_0 * 0.01745329251994329547;
-}
-
-kernel void test_function() {
-  half3 val = 0.0h;
-  half3 const tint_symbol = tint_radians(val);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Pack2x16Float) {
-    auto* call = Call("pack2x16float", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "as_type<uint>(half2(p1))");
-}
-
-TEST_F(MslASTPrinterTest, Unpack2x16Float) {
-    auto* call = Call("unpack2x16float", "p1");
-    GlobalVar("p1", ty.u32(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float2(as_type<half2>(p1))");
-}
-
-TEST_F(MslASTPrinterTest, DotI32) {
-    GlobalVar("v", ty.vec3<i32>(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", Call("dot", "v", "v"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T>
-T tint_dot3(vec<T,3> a, vec<T,3> b) {
-  return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
-}
-struct tint_private_vars_struct {
-  int3 v;
-};
-
-kernel void test_function() {
-  thread tint_private_vars_struct tint_private_vars = {};
-  int r = tint_dot3(tint_private_vars.v, tint_private_vars.v);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Ignore) {
-    Func("f", Vector{Param("a", ty.i32()), Param("b", ty.i32()), Param("c", ty.i32())}, ty.i32(),
-         Vector{Return(Mul(Add("a", "b"), "c"))});
-
-    Func("func", tint::Empty, ty.void_(), Vector{CallStmt(Call("f", 1_i, 2_i, 3_i))},
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-int f(int a, int b, int c) {
-  return as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(a) + as_type<uint>(b)))) * as_type<uint>(c)));
-}
-
-kernel void func() {
-  f(1, 2, 3);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, PolyfillDot4I8Packed) {
-    WrapInFunction(Decl(Let("zero", Expr(0_u))),  //
-                   Decl(Let("v", Call("dot4I8Packed", "zero", Expr(1_u)))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T>
-T tint_dot4(vec<T,4> a, vec<T,4> b) {
-  return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
-}
-int tint_dot4_i8_packed(uint a, uint b) {
-  int4 const a_i8 = (as_type<int4>((uint4(a) << uint4(24u, 16u, 8u, 0u))) >> uint4(24u));
-  int4 const b_i8 = (as_type<int4>((uint4(b) << uint4(24u, 16u, 8u, 0u))) >> uint4(24u));
-  return tint_dot4(a_i8, b_i8);
-}
-
-kernel void test_function() {
-  uint const zero = 0u;
-  int const v = tint_dot4_i8_packed(zero, 1u);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, PolyfillDot4U8Packed) {
-    WrapInFunction(Decl(Let("zero", Expr(0_u))),  //
-                   Decl(Let("v", Call("dot4U8Packed", "zero", Expr(1_u)))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T>
-T tint_dot4(vec<T,4> a, vec<T,4> b) {
-  return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
-}
-uint tint_dot4_u8_packed(uint a, uint b) {
-  uint4 const a_u8 = ((uint4(a) >> uint4(24u, 16u, 8u, 0u)) & uint4(255u));
-  uint4 const b_u8 = ((uint4(b) >> uint4(24u, 16u, 8u, 0u)) & uint4(255u));
-  return tint_dot4(a_u8, b_u8);
-}
-
-kernel void test_function() {
-  uint const zero = 0u;
-  uint const v = tint_dot4_u8_packed(zero, 1u);
-  return;
-}
-
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/builtin_texture_test.cc b/src/tint/lang/msl/writer/ast_printer/builtin_texture_test.cc
deleted file mode 100644
index 8213b90..0000000
--- a/src/tint/lang/msl/writer/ast_printer/builtin_texture_test.cc
+++ /dev/null
@@ -1,313 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/builtin_texture_helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::msl::writer {
-namespace {
-
-std::string expected_texture_overload(ast::test::ValidTextureOverload overload) {
-    using ValidTextureOverload = ast::test::ValidTextureOverload;
-    switch (overload) {
-        case ValidTextureOverload::kDimensions1d:
-        case ValidTextureOverload::kDimensionsStorageWO1d:
-            return R"(Texture.get_width(0))";
-        case ValidTextureOverload::kDimensions2d:
-        case ValidTextureOverload::kDimensions2dArray:
-        case ValidTextureOverload::kDimensionsCube:
-        case ValidTextureOverload::kDimensionsCubeArray:
-        case ValidTextureOverload::kDimensionsMultisampled2d:
-        case ValidTextureOverload::kDimensionsDepth2d:
-        case ValidTextureOverload::kDimensionsDepth2dArray:
-        case ValidTextureOverload::kDimensionsDepthCube:
-        case ValidTextureOverload::kDimensionsDepthCubeArray:
-        case ValidTextureOverload::kDimensionsDepthMultisampled2d:
-        case ValidTextureOverload::kDimensionsStorageWO2d:
-        case ValidTextureOverload::kDimensionsStorageWO2dArray:
-            return R"(uint2(Texture.get_width(), Texture.get_height()))";
-        case ValidTextureOverload::kDimensions3d:
-        case ValidTextureOverload::kDimensionsStorageWO3d:
-            return R"(uint3(Texture.get_width(), Texture.get_height(), Texture.get_depth()))";
-        case ValidTextureOverload::kDimensions2dLevel:
-        case ValidTextureOverload::kDimensionsCubeLevel:
-        case ValidTextureOverload::kDimensionsCubeArrayLevel:
-        case ValidTextureOverload::kDimensions2dArrayLevel:
-        case ValidTextureOverload::kDimensionsDepth2dLevel:
-        case ValidTextureOverload::kDimensionsDepth2dArrayLevel:
-        case ValidTextureOverload::kDimensionsDepthCubeLevel:
-        case ValidTextureOverload::kDimensionsDepthCubeArrayLevel:
-            return R"(uint2(Texture.get_width(1), Texture.get_height(1)))";
-        case ValidTextureOverload::kDimensions3dLevel:
-            return R"(uint3(Texture.get_width(1), Texture.get_height(1), Texture.get_depth(1)))";
-        case ValidTextureOverload::kGather2dF32:
-            return R"(Texture.gather(Sampler, float2(1.0f, 2.0f), int2(0), component::x))";
-        case ValidTextureOverload::kGather2dOffsetF32:
-            return R"(Texture.gather(Sampler, float2(1.0f, 2.0f), int2(3, 4), component::x))";
-        case ValidTextureOverload::kGather2dArrayF32:
-            return R"(Texture.gather(Sampler, float2(1.0f, 2.0f), max(0, 3), int2(0), component::x))";
-        case ValidTextureOverload::kGather2dArrayOffsetF32:
-            return R"(Texture.gather(Sampler, float2(1.0f, 2.0f), 3u, int2(4, 5), component::x))";
-        case ValidTextureOverload::kGatherCubeF32:
-            return R"(Texture.gather(Sampler, float3(1.0f, 2.0f, 3.0f), component::x))";
-        case ValidTextureOverload::kGatherCubeArrayF32:
-            return R"(Texture.gather(Sampler, float3(1.0f, 2.0f, 3.0f), 4u, component::x))";
-        case ValidTextureOverload::kGatherDepth2dF32:
-            return R"(Texture.gather(Sampler, float2(1.0f, 2.0f)))";
-        case ValidTextureOverload::kGatherDepth2dOffsetF32:
-            return R"(Texture.gather(Sampler, float2(1.0f, 2.0f), int2(3, 4)))";
-        case ValidTextureOverload::kGatherDepth2dArrayF32:
-            return R"(Texture.gather(Sampler, float2(1.0f, 2.0f), 3u))";
-        case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
-            return R"(Texture.gather(Sampler, float2(1.0f, 2.0f), max(0, 3), int2(4, 5)))";
-        case ValidTextureOverload::kGatherDepthCubeF32:
-            return R"(Texture.gather(Sampler, float3(1.0f, 2.0f, 3.0f)))";
-        case ValidTextureOverload::kGatherDepthCubeArrayF32:
-            return R"(Texture.gather(Sampler, float3(1.0f, 2.0f, 3.0f), 4u))";
-        case ValidTextureOverload::kGatherCompareDepth2dF32:
-            return R"(Texture.gather_compare(Sampler, float2(1.0f, 2.0f), 3.0f))";
-        case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
-            return R"(Texture.gather_compare(Sampler, float2(1.0f, 2.0f), 3.0f, int2(4, 5)))";
-        case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
-            return R"(Texture.gather_compare(Sampler, float2(1.0f, 2.0f), max(0, 3), 4.0f))";
-        case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
-            return R"(Texture.gather_compare(Sampler, float2(1.0f, 2.0f), max(0, 3), 4.0f, int2(5, 6)))";
-        case ValidTextureOverload::kGatherCompareDepthCubeF32:
-            return R"(Texture.gather_compare(Sampler, float3(1.0f, 2.0f, 3.0f), 4.0f))";
-        case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
-            return R"(Texture.gather_compare(Sampler, float3(1.0f, 2.0f, 3.0f), 4u, 5.0f))";
-        case ValidTextureOverload::kNumLayers2dArray:
-        case ValidTextureOverload::kNumLayersCubeArray:
-        case ValidTextureOverload::kNumLayersDepth2dArray:
-        case ValidTextureOverload::kNumLayersDepthCubeArray:
-        case ValidTextureOverload::kNumLayersStorageWO2dArray:
-            return R"(Texture.get_array_size())";
-        case ValidTextureOverload::kNumLevels2d:
-        case ValidTextureOverload::kNumLevels2dArray:
-        case ValidTextureOverload::kNumLevels3d:
-        case ValidTextureOverload::kNumLevelsCube:
-        case ValidTextureOverload::kNumLevelsCubeArray:
-        case ValidTextureOverload::kNumLevelsDepth2d:
-        case ValidTextureOverload::kNumLevelsDepth2dArray:
-        case ValidTextureOverload::kNumLevelsDepthCube:
-        case ValidTextureOverload::kNumLevelsDepthCubeArray:
-            return R"(Texture.get_num_mip_levels())";
-        case ValidTextureOverload::kNumSamplesDepthMultisampled2d:
-        case ValidTextureOverload::kNumSamplesMultisampled2d:
-            return R"(Texture.get_num_samples())";
-        case ValidTextureOverload::kSample1dF32:
-            return R"(Texture.sample(Sampler, 1.0f))";
-        case ValidTextureOverload::kSample2dF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f)))";
-        case ValidTextureOverload::kSample2dOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), int2(3, 4)))";
-        case ValidTextureOverload::kSample2dArrayF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), max(0, 3)))";
-        case ValidTextureOverload::kSample2dArrayOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), 3u, int2(4, 5)))";
-        case ValidTextureOverload::kSample3dF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f)))";
-        case ValidTextureOverload::kSample3dOffsetF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), int3(4, 5, 6)))";
-        case ValidTextureOverload::kSampleCubeF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f)))";
-        case ValidTextureOverload::kSampleCubeArrayF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), max(0, 4)))";
-        case ValidTextureOverload::kSampleDepth2dF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f)))";
-        case ValidTextureOverload::kSampleDepth2dOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), int2(3, 4)))";
-        case ValidTextureOverload::kSampleDepth2dArrayF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), max(0, 3)))";
-        case ValidTextureOverload::kSampleDepth2dArrayOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), max(0, 3), int2(4, 5)))";
-        case ValidTextureOverload::kSampleDepthCubeF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f)))";
-        case ValidTextureOverload::kSampleDepthCubeArrayF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), 4u))";
-        case ValidTextureOverload::kSampleBias2dF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), bias(3.0f)))";
-        case ValidTextureOverload::kSampleBias2dOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), bias(3.0f), int2(4, 5)))";
-        case ValidTextureOverload::kSampleBias2dArrayF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), 4u, bias(3.0f)))";
-        case ValidTextureOverload::kSampleBias2dArrayOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), max(0, 3), bias(4.0f), int2(5, 6)))";
-        case ValidTextureOverload::kSampleBias3dF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), bias(4.0f)))";
-        case ValidTextureOverload::kSampleBias3dOffsetF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), bias(4.0f), int3(5, 6, 7)))";
-        case ValidTextureOverload::kSampleBiasCubeF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), bias(4.0f)))";
-        case ValidTextureOverload::kSampleBiasCubeArrayF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), max(0, 3), bias(4.0f)))";
-        case ValidTextureOverload::kSampleLevel2dF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), level(3.0f)))";
-        case ValidTextureOverload::kSampleLevel2dOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), level(3.0f), int2(4, 5)))";
-        case ValidTextureOverload::kSampleLevel2dArrayF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), max(0, 3), level(4.0f)))";
-        case ValidTextureOverload::kSampleLevel2dArrayOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), max(0, 3), level(4.0f), int2(5, 6)))";
-        case ValidTextureOverload::kSampleLevel3dF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), level(4.0f)))";
-        case ValidTextureOverload::kSampleLevel3dOffsetF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), level(4.0f), int3(5, 6, 7)))";
-        case ValidTextureOverload::kSampleLevelCubeF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), level(4.0f)))";
-        case ValidTextureOverload::kSampleLevelCubeArrayF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), max(0, 4), level(5.0f)))";
-        case ValidTextureOverload::kSampleLevelDepth2dF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), level(3u)))";
-        case ValidTextureOverload::kSampleLevelDepth2dOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), level(3), int2(4, 5)))";
-        case ValidTextureOverload::kSampleLevelDepth2dArrayF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), 3u, level(4u)))";
-        case ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), 3u, level(4u), int2(5, 6)))";
-        case ValidTextureOverload::kSampleLevelDepthCubeF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), level(4)))";
-        case ValidTextureOverload::kSampleLevelDepthCubeArrayF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), max(0, 4), level(5)))";
-        case ValidTextureOverload::kSampleGrad2dF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), gradient2d(float2(3.0f, 4.0f), float2(5.0f, 6.0f))))";
-        case ValidTextureOverload::kSampleGrad2dOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), gradient2d(float2(3.0f, 4.0f), float2(5.0f, 6.0f)), int2(7)))";
-        case ValidTextureOverload::kSampleGrad2dArrayF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), max(0, 3), gradient2d(float2(4.0f, 5.0f), float2(6.0f, 7.0f))))";
-        case ValidTextureOverload::kSampleGrad2dArrayOffsetF32:
-            return R"(Texture.sample(Sampler, float2(1.0f, 2.0f), 3u, gradient2d(float2(4.0f, 5.0f), float2(6.0f, 7.0f)), int2(6, 7)))";
-        case ValidTextureOverload::kSampleGrad3dF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), gradient3d(float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))))";
-        case ValidTextureOverload::kSampleGrad3dOffsetF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), gradient3d(float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f)), int3(0, 1, 2)))";
-        case ValidTextureOverload::kSampleGradCubeF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), gradientcube(float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))))";
-        case ValidTextureOverload::kSampleGradCubeArrayF32:
-            return R"(Texture.sample(Sampler, float3(1.0f, 2.0f, 3.0f), 4u, gradientcube(float3(5.0f, 6.0f, 7.0f), float3(8.0f, 9.0f, 10.0f))))";
-        case ValidTextureOverload::kSampleCompareDepth2dF32:
-            return R"(Texture.sample_compare(Sampler, float2(1.0f, 2.0f), 3.0f))";
-        case ValidTextureOverload::kSampleCompareDepth2dOffsetF32:
-            return R"(Texture.sample_compare(Sampler, float2(1.0f, 2.0f), 3.0f, int2(4, 5)))";
-        case ValidTextureOverload::kSampleCompareDepth2dArrayF32:
-            return R"(Texture.sample_compare(Sampler, float2(1.0f, 2.0f), max(0, 4), 3.0f))";
-        case ValidTextureOverload::kSampleCompareDepth2dArrayOffsetF32:
-            return R"(Texture.sample_compare(Sampler, float2(1.0f, 2.0f), 4u, 3.0f, int2(5, 6)))";
-        case ValidTextureOverload::kSampleCompareDepthCubeF32:
-            return R"(Texture.sample_compare(Sampler, float3(1.0f, 2.0f, 3.0f), 4.0f))";
-        case ValidTextureOverload::kSampleCompareDepthCubeArrayF32:
-            return R"(Texture.sample_compare(Sampler, float3(1.0f, 2.0f, 3.0f), max(0, 4), 5.0f))";
-        case ValidTextureOverload::kSampleCompareLevelDepth2dF32:
-            return R"(Texture.sample_compare(Sampler, float2(1.0f, 2.0f), 3.0f, level(0)))";
-        case ValidTextureOverload::kSampleCompareLevelDepth2dOffsetF32:
-            return R"(Texture.sample_compare(Sampler, float2(1.0f, 2.0f), 3.0f, level(0), int2(4, 5)))";
-        case ValidTextureOverload::kSampleCompareLevelDepth2dArrayF32:
-            return R"(Texture.sample_compare(Sampler, float2(1.0f, 2.0f), max(0, 3), 4.0f, level(0)))";
-        case ValidTextureOverload::kSampleCompareLevelDepth2dArrayOffsetF32:
-            return R"(Texture.sample_compare(Sampler, float2(1.0f, 2.0f), max(0, 3), 4.0f, level(0), int2(5, 6)))";
-        case ValidTextureOverload::kSampleCompareLevelDepthCubeF32:
-            return R"(Texture.sample_compare(Sampler, float3(1.0f, 2.0f, 3.0f), 4.0f, level(0)))";
-        case ValidTextureOverload::kSampleCompareLevelDepthCubeArrayF32:
-            return R"(Texture.sample_compare(Sampler, float3(1.0f, 2.0f, 3.0f), max(0, 4), 5.0f, level(0)))";
-        case ValidTextureOverload::kLoad1dLevelF32:
-            return R"(Texture.read(uint(1u), 0))";
-        case ValidTextureOverload::kLoad1dLevelU32:
-            return R"(Texture.read(uint(1), 0))";
-        case ValidTextureOverload::kLoad1dLevelI32:
-            return R"(Texture.read(uint(1), 0))";
-        case ValidTextureOverload::kLoad2dLevelF32:
-            return R"(Texture.read(uint2(uint2(1u, 2u)), 3u))";
-        case ValidTextureOverload::kLoad2dLevelU32:
-            return R"(Texture.read(uint2(int2(1, 2)), 3))";
-        case ValidTextureOverload::kLoad2dArrayLevelF32:
-            return R"(Texture.read(uint2(int2(1, 2)), 3, 4))";
-        case ValidTextureOverload::kLoad2dArrayLevelU32:
-            return R"(Texture.read(uint2(int2(1, 2)), 3, 4))";
-        case ValidTextureOverload::kLoad2dArrayLevelI32:
-            return R"(Texture.read(uint2(uint2(1u, 2u)), 3u, 4u))";
-        case ValidTextureOverload::kLoad3dLevelF32:
-            return R"(Texture.read(uint3(int3(1, 2, 3)), 4))";
-        case ValidTextureOverload::kLoad3dLevelU32:
-            return R"(Texture.read(uint3(int3(1, 2, 3)), 4))";
-        case ValidTextureOverload::kLoad3dLevelI32:
-            return R"(Texture.read(uint3(uint3(1u, 2u, 3u)), 4u))";
-        case ValidTextureOverload::kLoadMultisampled2dF32:
-        case ValidTextureOverload::kLoadMultisampled2dU32:
-            return R"(Texture.read(uint2(int2(1, 2)), 3))";
-        case ValidTextureOverload::kLoad2dLevelI32:
-        case ValidTextureOverload::kLoadMultisampled2dI32:
-            return R"(Texture.read(uint2(uint2(1u, 2u)), 3u))";
-        case ValidTextureOverload::kLoadDepth2dLevelF32:
-            return R"(Texture.read(uint2(int2(1, 2)), 3))";
-        case ValidTextureOverload::kLoadDepth2dArrayLevelF32:
-            return R"(Texture.read(uint2(uint2(1u, 2u)), 3u, 4u))";
-        case ValidTextureOverload::kLoadDepthMultisampled2dF32:
-            return R"(Texture.read(uint2(uint2(1u, 2u)), 3u))";
-        case ValidTextureOverload::kStoreWO1dRgba32float:
-            return R"(Texture.write(float4(2.0f, 3.0f, 4.0f, 5.0f), uint(1)))";
-        case ValidTextureOverload::kStoreWO2dRgba32float:
-            return R"(Texture.write(float4(3.0f, 4.0f, 5.0f, 6.0f), uint2(int2(1, 2))))";
-        case ValidTextureOverload::kStoreWO2dArrayRgba32float:
-            return R"(Texture.write(float4(4.0f, 5.0f, 6.0f, 7.0f), uint2(uint2(1u, 2u)), 3u))";
-        case ValidTextureOverload::kStoreWO3dRgba32float:
-            return R"(Texture.write(float4(4.0f, 5.0f, 6.0f, 7.0f), uint3(uint3(1u, 2u, 3u))))";
-    }
-    return "<unmatched texture overload>";
-}  // NOLINT - Ignore the length of this function
-
-class MslGeneratorBuiltinTextureTest : public TestParamHelper<ast::test::TextureOverloadCase> {};
-
-TEST_P(MslGeneratorBuiltinTextureTest, Call) {
-    auto param = GetParam();
-
-    param.BuildTextureVariable(this);
-    param.BuildSamplerVariable(this);
-
-    auto* call = Call(param.function, param.args(this));
-    auto* stmt = param.returns_value ? static_cast<const ast::Statement*>(Assign(Phony(), call))
-                                     : static_cast<const ast::Statement*>(CallStmt(call));
-
-    Func("main", tint::Empty, ty.void_(), Vector{stmt},
-         Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-
-    auto expected = expected_texture_overload(param.overload);
-    EXPECT_EQ(expected, out.str());
-}
-
-INSTANTIATE_TEST_SUITE_P(MslGeneratorBuiltinTextureTest,
-                         MslGeneratorBuiltinTextureTest,
-                         testing::ValuesIn(ast::test::TextureOverloadCase::ValidCases()));
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/call_test.cc b/src/tint/lang/msl/writer/ast_printer/call_test.cc
deleted file mode 100644
index 9865f9f..0000000
--- a/src/tint/lang/msl/writer/ast_printer/call_test.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, EmitExpression_Call_WithoutParams) {
-    Func("my_func", tint::Empty, ty.f32(), Vector{Return(1.23_f)});
-
-    auto* call = Call("my_func");
-    WrapInFunction(call);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "my_func()");
-}
-
-TEST_F(MslASTPrinterTest, EmitExpression_Call_WithParams) {
-    Func("my_func",
-         Vector{
-             Param(Sym(), ty.f32()),
-             Param(Sym(), ty.f32()),
-         },
-         ty.f32(),
-         Vector{
-             Return(1.23_f),
-         });
-    GlobalVar("param1", ty.f32(), core::AddressSpace::kPrivate);
-    GlobalVar("param2", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* call = Call("my_func", "param1", "param2");
-    WrapInFunction(call);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "my_func(param1, param2)");
-}
-
-TEST_F(MslASTPrinterTest, EmitStatement_Call) {
-    Func("my_func",
-         Vector{
-             Param(Sym(), ty.f32()),
-             Param(Sym(), ty.f32()),
-         },
-         ty.void_(), tint::Empty, tint::Empty);
-    GlobalVar("param1", ty.f32(), core::AddressSpace::kPrivate);
-    GlobalVar("param2", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* call = Call("my_func", "param1", "param2");
-    auto* stmt = CallStmt(call);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  my_func(param1, param2);\n");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/case_test.cc b/src/tint/lang/msl/writer/ast_printer/case_test.cc
deleted file mode 100644
index fa3d56b..0000000
--- a/src/tint/lang/msl/writer/ast_printer/case_test.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_Case) {
-    auto* s =
-        Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  case 5: {
-    break;
-  }
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Case_BreaksByDefault) {
-    auto* s = Switch(1_i, Case(CaseSelector(5_i), Block()), DefaultCase());
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  case 5: {
-    break;
-  }
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Case_MultipleSelectors) {
-    auto* s = Switch(1_i,
-                     Case(
-                         Vector{
-                             CaseSelector(5_i),
-                             CaseSelector(6_i),
-                         },
-                         Block(create<ast::BreakStatement>())),
-                     DefaultCase());
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  case 5:
-  case 6: {
-    break;
-  }
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Case_Default) {
-    auto* s = Switch(1_i, DefaultCase(Block(create<ast::BreakStatement>())));
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitCase(s->body[0])) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  default: {
-    break;
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/cast_test.cc b/src/tint/lang/msl/writer/ast_printer/cast_test.cc
deleted file mode 100644
index 14cf4ea..0000000
--- a/src/tint/lang/msl/writer/ast_printer/cast_test.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, EmitExpression_Cast_Scalar) {
-    auto* cast = Call<f32>(1_i);
-    WrapInFunction(cast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "1.0f");
-}
-
-TEST_F(MslASTPrinterTest, EmitExpression_Cast_Vector) {
-    auto* cast = Call<vec3<f32>>(Call<vec3<i32>>(1_i, 2_i, 3_i));
-    WrapInFunction(cast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float3(1.0f, 2.0f, 3.0f)");
-}
-
-TEST_F(MslASTPrinterTest, EmitExpression_Cast_IntMin) {
-    auto* cast = Call<u32>(i32(std::numeric_limits<int32_t>::min()));
-    WrapInFunction(cast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "2147483648u");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/const_assert_test.cc b/src/tint/lang/msl/writer/ast_printer/const_assert_test.cc
deleted file mode 100644
index 12c14d7..0000000
--- a/src/tint/lang/msl/writer/ast_printer/const_assert_test.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConstAssert) {
-    GlobalConstAssert(true);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    // const asserts are not emitted
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_FunctionConstAssert) {
-    Func("f", tint::Empty, ty.void_(), Vector{ConstAssert(true)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    // const asserts are not emitted
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-}
-
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/constructor_test.cc b/src/tint/lang/msl/writer/ast_printer/constructor_test.cc
deleted file mode 100644
index 682666d..0000000
--- a/src/tint/lang/msl/writer/ast_printer/constructor_test.cc
+++ /dev/null
@@ -1,435 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using ::testing::HasSubstr;
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using MslASTPrinterTest_Constructor = TestHelper;
-
-TEST_F(MslASTPrinterTest_Constructor, Bool) {
-    WrapInFunction(Expr(false));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("false"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Int) {
-    WrapInFunction(Expr(-12345_i));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("-12345"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, UInt) {
-    WrapInFunction(Expr(56779_u));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("56779u"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Float) {
-    // Use a number close to 1<<30 but whose decimal representation ends in 0.
-    WrapInFunction(Expr(f32((1 << 30) - 4)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("1073741824.0f"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, F16) {
-    Enable(wgsl::Extension::kF16);
-
-    // Use a number close to 1<<16 but whose decimal representation ends in 0.
-    WrapInFunction(Expr(f16((1 << 15) - 8)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("32752.0h"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Float) {
-    WrapInFunction(Call<f32>(-1.2e-5_f));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("-0.00001200000042445026f"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<f16>(-1.2e-3_h));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("-0.0011997222900390625h"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Bool) {
-    WrapInFunction(Call<bool>(true));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("true"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Int) {
-    WrapInFunction(Call<i32>(-12345_i));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("-12345"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Uint) {
-    WrapInFunction(Call<u32>(12345_u));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("12345u"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_F32) {
-    WrapInFunction(Call<vec3<f32>>(1_f, 2_f, 3_f));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("float3(1.0f, 2.0f, 3.0f)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<vec3<f16>>(1_h, 2_h, 3_h));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("half3(1.0h, 2.0h, 3.0h)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_Empty_F32) {
-    WrapInFunction(Call<vec3<f32>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("float3(0.0f)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_Empty_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<vec3<f16>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("half3(0.0h)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_SingleScalar_F32_Literal) {
-    WrapInFunction(Call<vec3<f32>>(2_f));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("float3(2.0f)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_SingleScalar_F16_Literal) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<vec3<f16>>(2_h));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("half3(2.0h)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_SingleScalar_F32_Var) {
-    auto* var = Var("v", Expr(2_f));
-    auto* cast = Call<vec3<f32>>(var);
-    WrapInFunction(var, cast);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(float v = 2.0f;
-  float3 const tint_symbol = float3(v);)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_SingleScalar_F16_Var) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = Var("v", Expr(2_h));
-    auto* cast = Call<vec3<f16>>(var);
-    WrapInFunction(var, cast);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(half v = 2.0h;
-  half3 const tint_symbol = half3(v);)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_SingleScalar_Bool) {
-    WrapInFunction(Call<vec3<bool>>(true));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("bool3(true)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_SingleScalar_Int) {
-    WrapInFunction(Call<vec3<i32>>(2_i));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("int3(2)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Vec_SingleScalar_UInt) {
-    WrapInFunction(Call<vec3<u32>>(2_u));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("uint3(2u)"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Mat_F32) {
-    WrapInFunction(
-        Call<mat2x3<f32>>(Call<vec3<f32>>(1_f, 2_f, 3_f), Call<vec3<f32>>(3_f, 4_f, 5_f)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(),
-                HasSubstr("float2x3(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f))"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Mat_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(
-        Call<mat2x3<f16>>(Call<vec3<f16>>(1_h, 2_h, 3_h), Call<vec3<f16>>(3_h, 4_h, 5_h)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(),
-                HasSubstr("half2x3(half3(1.0h, 2.0h, 3.0h), half3(3.0h, 4.0h, 5.0h))"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Mat_Complex_F32) {
-    // mat4x4<f32>(
-    //     vec4<f32>(2.0f, 3.0f, 4.0f, 8.0f),
-    //     vec4<f32>(),
-    //     vec4<f32>(7.0f),
-    //     vec4<f32>(vec4<f32>(42.0f, 21.0f, 6.0f, -5.0f)),
-    //   );
-    auto* vector_literal = Call<vec4<f32>>(f32(2.0), f32(3.0), f32(4.0), f32(8.0));
-    auto* vector_zero_init = Call<vec4<f32>>();
-    auto* vector_single_scalar_init = Call<vec4<f32>>(f32(7.0));
-    auto* vector_identical_init =
-        Call<vec4<f32>>(Call<vec4<f32>>(f32(42.0), f32(21.0), f32(6.0), f32(-5.0)));
-
-    auto* constructor = Call<mat4x4<f32>>(vector_literal, vector_zero_init,
-                                          vector_single_scalar_init, vector_identical_init);
-
-    WrapInFunction(constructor);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(), HasSubstr("float4x4(float4(2.0f, 3.0f, 4.0f, 8.0f), float4(0.0f), "
-                                        "float4(7.0f), float4(42.0f, 21.0f, 6.0f, -5.0f))"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Mat_Complex_F16) {
-    // mat4x4<f16>(
-    //     vec4<f16>(2.0h, 3.0h, 4.0h, 8.0h),
-    //     vec4<f16>(),
-    //     vec4<f16>(7.0h),
-    //     vec4<f16>(vec4<f16>(42.0h, 21.0h, 6.0h, -5.0h)),
-    //   );
-    Enable(wgsl::Extension::kF16);
-
-    auto* vector_literal = Call<vec4<f16>>(f16(2.0), f16(3.0), f16(4.0), f16(8.0));
-    auto* vector_zero_init = Call<vec4<f16>>();
-    auto* vector_single_scalar_init = Call<vec4<f16>>(f16(7.0));
-    auto* vector_identical_init =
-        Call<vec4<f16>>(Call<vec4<f16>>(f16(42.0), f16(21.0), f16(6.0), f16(-5.0)));
-
-    auto* constructor = Call<mat4x4<f16>>(vector_literal, vector_zero_init,
-                                          vector_single_scalar_init, vector_identical_init);
-
-    WrapInFunction(constructor);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(), HasSubstr("half4x4(half4(2.0h, 3.0h, 4.0h, 8.0h), half4(0.0h), "
-                                        "half4(7.0h), half4(42.0h, 21.0h, 6.0h, -5.0h))"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Mat_Empty_F32) {
-    WrapInFunction(Call<mat2x3<f32>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(),
-                HasSubstr("float2x3 const tint_symbol = float2x3(float3(0.0f), float3(0.0f))"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Mat_Empty_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<mat2x3<f16>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(),
-                HasSubstr("half2x3 const tint_symbol = half2x3(half3(0.0h), half3(0.0h))"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Mat_Identity_F32) {
-    // fn f() {
-    //     var m_1: mat4x4<f32> = mat4x4<f32>();
-    //     var m_2: mat4x4<f32> = mat4x4<f32>(m_1);
-    // }
-
-    auto* m_1 = Var("m_1", ty.mat4x4(ty.f32()), Call<mat4x4<f32>>());
-    auto* m_2 = Var("m_2", ty.mat4x4(ty.f32()), Call<mat4x4<f32>>(m_1));
-
-    WrapInFunction(m_1, m_2);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(), HasSubstr("float4x4 m_2 = float4x4(m_1);"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Mat_Identity_F16) {
-    // fn f() {
-    //     var m_1: mat4x4<f16> = mat4x4<f16>();
-    //     var m_2: mat4x4<f16> = mat4x4<f16>(m_1);
-    // }
-
-    Enable(wgsl::Extension::kF16);
-
-    auto* m_1 = Var("m_1", ty.mat4x4(ty.f16()), Call<mat4x4<f16>>());
-    auto* m_2 = Var("m_2", ty.mat4x4(ty.f16()), Call<mat4x4<f16>>(m_1));
-
-    WrapInFunction(m_1, m_2);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(), HasSubstr("half4x4 m_2 = half4x4(m_1);"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Array) {
-    WrapInFunction(Call<array<vec3<f32>, 3>>(Call<vec3<f32>>(1_f, 2_f, 3_f),
-                                             Call<vec3<f32>>(4_f, 5_f, 6_f),
-                                             Call<vec3<f32>>(7_f, 8_f, 9_f)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("{float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), "
-                                        "float3(7.0f, 8.0f, 9.0f)}"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Struct) {
-    auto* str = Structure("S", Vector{
-                                   Member("a", ty.i32()),
-                                   Member("b", ty.f32()),
-                                   Member("c", ty.vec3<i32>()),
-                               });
-
-    WrapInFunction(Call(ty.Of(str), 1_i, 2_f, Call<vec3<i32>>(3_i, 4_i, 5_i)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("{.a=1, .b=2.0f, .c=int3(3, 4, 5)}"));
-}
-
-TEST_F(MslASTPrinterTest_Constructor, Type_Struct_Empty) {
-    auto* str = Structure("S", Vector{
-                                   Member("a", ty.i32()),
-                                   Member("b", ty.f32()),
-                                   Member("c", ty.vec3<i32>()),
-                               });
-
-    WrapInFunction(Call(ty.Of(str)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("{}"));
-    EXPECT_THAT(gen.Result(), testing::Not(HasSubstr("{{}}")));
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/continue_test.cc b/src/tint/lang/msl/writer/ast_printer/continue_test.cc
deleted file mode 100644
index 266daaf..0000000
--- a/src/tint/lang/msl/writer/ast_printer/continue_test.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_Continue) {
-    auto* loop = Loop(Block(If(false, Block(Break())),  //
-                            Continue()));
-    WrapInFunction(loop);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-#define TINT_ISOLATE_UB(VOLATILE_NAME) \
-  {volatile bool VOLATILE_NAME = false; if (VOLATILE_NAME) break;}
-
-kernel void test_function() {
-  while(true) {
-    TINT_ISOLATE_UB(tint_volatile_false);
-    if (false) {
-      break;
-    }
-    continue;
-  }
-  return;
-}
-
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/discard_test.cc b/src/tint/lang/msl/writer/ast_printer/discard_test.cc
deleted file mode 100644
index d1fdd53..0000000
--- a/src/tint/lang/msl/writer/ast_printer/discard_test.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-// MslPrinterTest.Discard
-TEST_F(MslASTPrinterTest, Emit_Discard) {
-    auto* stmt = Discard();
-
-    Func("F", tint::Empty, ty.void_(), Vector{stmt}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  discard_fragment();\n");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/helper_test.h b/src/tint/lang/msl/writer/ast_printer/helper_test.h
deleted file mode 100644
index cdadbfb..0000000
--- a/src/tint/lang/msl/writer/ast_printer/helper_test.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_MSL_WRITER_AST_PRINTER_HELPER_TEST_H_
-#define SRC_TINT_LANG_MSL_WRITER_AST_PRINTER_HELPER_TEST_H_
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "src/tint/lang/msl/writer/ast_printer/ast_printer.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-
-namespace tint::msl::writer {
-
-/// Helper class for testing
-template <typename BASE>
-class TestHelperBase : public BASE, public ProgramBuilder {
-  public:
-    TestHelperBase() = default;
-    ~TestHelperBase() override = default;
-
-    /// @returns the default generator options for SanitizeAndBuild(), if no explicit options are
-    /// provided.
-    static Options DefaultOptions() {
-        Options opts;
-        opts.disable_robustness = true;
-        return opts;
-    }
-
-    /// Builds and returns a ASTPrinter from the program.
-    /// @note The generator is only built once. Multiple calls to Build() will
-    /// return the same ASTPrinter without rebuilding.
-    /// @return the built generator
-    ASTPrinter& Build(const Options& options = {}) {
-        if (gen_) {
-            return *gen_;
-        }
-        if (!IsValid()) {
-            ADD_FAILURE() << "ProgramBuilder is not valid: " << Diagnostics();
-        }
-        program = std::make_unique<Program>(resolver::Resolve(*this));
-        if (!program->IsValid()) {
-            ADD_FAILURE() << program->Diagnostics();
-        }
-        gen_ = std::make_unique<ASTPrinter>(*program, options);
-        return *gen_;
-    }
-
-    /// Builds the program, runs the program through the transform::Msl sanitizer
-    /// and returns a ASTPrinter from the sanitized program.
-    /// @param options The MSL generator options.
-    /// @note The generator is only built once. Multiple calls to Build() will
-    /// return the same ASTPrinter without rebuilding.
-    /// @return the built generator
-    ASTPrinter& SanitizeAndBuild(const Options& options = DefaultOptions()) {
-        if (gen_) {
-            return *gen_;
-        }
-        if (!IsValid()) {
-            ADD_FAILURE() << "ProgramBuilder is not valid: " << Diagnostics();
-        }
-        program = std::make_unique<Program>(resolver::Resolve(*this));
-        if (!program->IsValid()) {
-            ADD_FAILURE() << program->Diagnostics();
-        }
-
-        auto result = Sanitize(*program, options);
-        if (!result.program.IsValid()) {
-            ADD_FAILURE() << result.program.Diagnostics();
-        }
-        *program = std::move(result.program);
-        gen_ = std::make_unique<ASTPrinter>(*program, options);
-        return *gen_;
-    }
-
-    /// The program built with a call to Build()
-    std::unique_ptr<Program> program;
-
-  private:
-    std::unique_ptr<ASTPrinter> gen_;
-    std::unique_ptr<ASTPrinter> gen_with_options_;
-};
-
-/// Test helper
-using TestHelper = TestHelperBase<testing::Test>;
-
-/// Param test helper
-template <typename T>
-using TestParamHelper = TestHelperBase<testing::TestWithParam<T>>;
-
-}  // namespace tint::msl::writer
-
-#endif  // SRC_TINT_LANG_MSL_WRITER_AST_PRINTER_HELPER_TEST_H_
diff --git a/src/tint/lang/msl/writer/ast_printer/identifier_test.cc b/src/tint/lang/msl/writer/ast_printer/identifier_test.cc
deleted file mode 100644
index 57c0561..0000000
--- a/src/tint/lang/msl/writer/ast_printer/identifier_test.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, EmitIdentifierExpression) {
-    auto* foo = Var("foo", ty.i32());
-
-    auto* i = Expr("foo");
-    WrapInFunction(foo, i);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "foo");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/if_test.cc b/src/tint/lang/msl/writer/ast_printer/if_test.cc
deleted file mode 100644
index 1f4a8f0..0000000
--- a/src/tint/lang/msl/writer/ast_printer/if_test.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-// MSLPrinterTest.If
-TEST_F(MslASTPrinterTest, Emit_If) {
-    auto* cond = Var("cond", ty.bool_());
-    auto* i = If(cond, Block(Return()));
-    WrapInFunction(cond, i);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  if (cond) {
-    return;
-  }
-)");
-}
-
-// MSLPrinterTest.IfWithElseIf
-TEST_F(MslASTPrinterTest, Emit_IfWithElseIf) {
-    auto* cond = Var("cond", ty.bool_());
-    auto* else_cond = Var("else_cond", ty.bool_());
-    auto* i = If(cond, Block(Return()), Else(If(else_cond, Block(Return()))));
-    WrapInFunction(cond, else_cond, i);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  if (cond) {
-    return;
-  } else {
-    if (else_cond) {
-      return;
-    }
-  }
-)");
-}
-
-// MSLPrinterTest.IfWithElse
-TEST_F(MslASTPrinterTest, Emit_IfWithElse) {
-    auto* cond = Var("cond", ty.bool_());
-    auto* i = If(cond, Block(Return()), Else(Block(Return())));
-    WrapInFunction(cond, i);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  if (cond) {
-    return;
-  } else {
-    return;
-  }
-)");
-}
-
-// TODO(dj2): Port to MSLPrinterTest
-TEST_F(MslASTPrinterTest, Emit_IfWithMultiple) {
-    auto* cond = Var("cond", ty.bool_());
-    auto* else_cond = Var("else_cond", ty.bool_());
-    auto* i =
-        If(cond, Block(Return()), Else(If(else_cond, Block(Return()), Else(Block(Return())))));
-    WrapInFunction(cond, else_cond, i);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  if (cond) {
-    return;
-  } else {
-    if (else_cond) {
-      return;
-    } else {
-      return;
-    }
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/import_test.cc b/src/tint/lang/msl/writer/ast_printer/import_test.cc
deleted file mode 100644
index 4ada591..0000000
--- a/src/tint/lang/msl/writer/ast_printer/import_test.cc
+++ /dev/null
@@ -1,292 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using MslASTPrinterTest = TestHelper;
-
-struct MslImportData {
-    const char* name;
-    const char* msl_name;
-};
-inline std::ostream& operator<<(std::ostream& out, MslImportData data) {
-    out << data.name;
-    return out;
-}
-using MslImportData_SingleParamTest = TestParamHelper<MslImportData>;
-TEST_P(MslImportData_SingleParamTest, FloatScalar) {
-    auto param = GetParam();
-    auto* call = Call(param.name, 1_f);
-
-    // The resolver will set the builtin data for the ident
-    WrapInFunction(call);
-
-    ASTPrinter& gen = Build();
-
-    auto* sem = program->Sem().Get<sem::Call>(call);
-    ASSERT_NE(sem, nullptr);
-    auto* target = sem->Target();
-    ASSERT_NE(target, nullptr);
-    auto* builtin = target->As<sem::BuiltinFn>();
-    ASSERT_NE(builtin, nullptr);
-
-    ASSERT_EQ(gen.generate_builtin_name(builtin), param.msl_name);
-}
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslImportData_SingleParamTest,
-                         testing::Values(MslImportData{"abs", "fabs"},
-                                         MslImportData{"acos", "acos"},
-                                         MslImportData{"asin", "asin"},
-                                         MslImportData{"atan", "atan"},
-                                         MslImportData{"ceil", "ceil"},
-                                         MslImportData{"cos", "cos"},
-                                         MslImportData{"cosh", "cosh"},
-                                         MslImportData{"exp", "exp"},
-                                         MslImportData{"exp2", "exp2"},
-                                         MslImportData{"floor", "floor"},
-                                         MslImportData{"fract", "fract"},
-                                         MslImportData{"inverseSqrt", "rsqrt"},
-                                         MslImportData{"length", "length"},
-                                         MslImportData{"log", "log"},
-                                         MslImportData{"log2", "log2"},
-                                         MslImportData{"round", "rint"},
-                                         MslImportData{"sign", "sign"},
-                                         MslImportData{"sin", "sin"},
-                                         MslImportData{"sinh", "sinh"},
-                                         MslImportData{"sqrt", "sqrt"},
-                                         MslImportData{"tan", "tan"},
-                                         MslImportData{"tanh", "tanh"},
-                                         MslImportData{"trunc", "trunc"}));
-
-TEST_F(MslASTPrinterTest, MslImportData_SingleParamTest_IntScalar) {
-    auto* expr = Call("abs", 1_i);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), R"(abs(1))");
-}
-
-TEST_F(MslASTPrinterTest, MslImportData_SingleParamTest_ScalarLength) {
-    auto* expr = Call("length", 2_f);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), R"(fabs(2.0f))");
-}
-
-using MslImportData_DualParam_ScalarTest = TestParamHelper<MslImportData>;
-TEST_P(MslImportData_DualParam_ScalarTest, Float) {
-    auto param = GetParam();
-    auto* expr = Call(param.name, 1_f, 2_f);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.msl_name) + "(1.0f, 2.0f)");
-}
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslImportData_DualParam_ScalarTest,
-                         testing::Values(MslImportData{"atan2", "atan2"},
-                                         MslImportData{"max", "fmax"},
-                                         MslImportData{"min", "fmin"},
-                                         MslImportData{"pow", "powr"},
-                                         MslImportData{"step", "step"}));
-
-TEST_F(MslASTPrinterTest, MslImportData_DualParam_ScalarDistance) {
-    auto* expr = Call("distance", 2_f, 3_f);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), R"(fabs(2.0f - 3.0f))");
-}
-
-using MslImportData_DualParam_VectorTest = TestParamHelper<MslImportData>;
-TEST_P(MslImportData_DualParam_VectorTest, Float) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, Call<vec3<f32>>(1_f, 2_f, 3_f), Call<vec3<f32>>(4_f, 5_f, 6_f));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.msl_name) +
-                             R"((float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f)))");
-}
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslImportData_DualParam_VectorTest,
-                         testing::Values(MslImportData{"atan2", "atan2"},
-                                         MslImportData{"cross", "cross"},
-                                         MslImportData{"distance", "distance"},
-                                         MslImportData{"max", "fmax"},
-                                         MslImportData{"min", "fmin"},
-                                         MslImportData{"pow", "powr"},
-                                         MslImportData{"reflect", "reflect"},
-                                         MslImportData{"step", "step"}));
-
-using MslImportData_DualParam_Int_Test = TestParamHelper<MslImportData>;
-TEST_P(MslImportData_DualParam_Int_Test, IntScalar) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, 1_i, 2_i);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.msl_name) + "(1, 2)");
-}
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslImportData_DualParam_Int_Test,
-                         testing::Values(MslImportData{"max", "max"}, MslImportData{"min", "min"}));
-
-using MslImportData_TripleParam_ScalarTest = TestParamHelper<MslImportData>;
-TEST_P(MslImportData_TripleParam_ScalarTest, Float) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, 1_f, 2_f, 3_f);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.msl_name) + "(1.0f, 2.0f, 3.0f)");
-}
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslImportData_TripleParam_ScalarTest,
-                         testing::Values(MslImportData{"fma", "fma"},
-                                         MslImportData{"mix", "mix"},
-                                         MslImportData{"clamp", "clamp"},
-                                         MslImportData{"smoothstep", "smoothstep"}));
-
-using MslImportData_TripleParam_VectorTest = TestParamHelper<MslImportData>;
-TEST_P(MslImportData_TripleParam_VectorTest, Float) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, Call<vec3<f32>>(1_f, 2_f, 3_f), Call<vec3<f32>>(4_f, 5_f, 6_f),
-                      Call<vec3<f32>>(7_f, 8_f, 9_f));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(
-        out.str(),
-        std::string(param.msl_name) +
-            R"((float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f)))");
-}
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslImportData_TripleParam_VectorTest,
-                         testing::Values(MslImportData{"faceForward", "faceforward"},
-                                         MslImportData{"fma", "fma"},
-                                         MslImportData{"clamp", "clamp"},
-                                         MslImportData{"smoothstep", "smoothstep"}));
-
-using MslImportData_TripleParam_Int_Test = TestParamHelper<MslImportData>;
-TEST_P(MslImportData_TripleParam_Int_Test, IntScalar) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, 1_i, 2_i, 3_i);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.msl_name) + "(1, 2, 3)");
-}
-INSTANTIATE_TEST_SUITE_P(MslASTPrinterTest,
-                         MslImportData_TripleParam_Int_Test,
-                         testing::Values(MslImportData{"clamp", "clamp"},
-                                         MslImportData{"clamp", "clamp"}));
-
-TEST_F(MslASTPrinterTest, MslImportData_Determinant) {
-    GlobalVar("var", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
-
-    auto* expr = Call("determinant", "var");
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string("determinant(var)"));
-}
-
-TEST_F(MslASTPrinterTest, MslImportData_QuantizeToF16_Scalar) {
-    GlobalVar("v", Expr(2_f), core::AddressSpace::kPrivate);
-
-    auto* expr = Call("quantizeToF16", "v");
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float(half(v))");
-}
-
-TEST_F(MslASTPrinterTest, MslImportData_QuantizeToF16_Vector) {
-    GlobalVar("v", Call<vec3<f32>>(2_f), core::AddressSpace::kPrivate);
-
-    auto* expr = Call("quantizeToF16", "v");
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float3(half3(v))");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/loop_test.cc b/src/tint/lang/msl/writer/ast_printer/loop_test.cc
deleted file mode 100644
index 9914e7e..0000000
--- a/src/tint/lang/msl/writer/ast_printer/loop_test.cc
+++ /dev/null
@@ -1,886 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-Options NoRobustness() {
-    Options o;
-    o.disable_robustness = true;
-    return o;
-}
-
-TEST_F(MslASTPrinterTest, Emit_Loop) {
-    auto* body = Block(Break());
-    auto* continuing = Block();
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-#define TINT_ISOLATE_UB(VOLATILE_NAME) \
-  {volatile bool VOLATILE_NAME = false; if (VOLATILE_NAME) break;}
-
-fragment void F() {
-  while(true) {
-    TINT_ISOLATE_UB(tint_volatile_false);
-    break;
-  }
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Loop_WithoutRobustness) {
-    auto* body = Block(Break());
-    auto* continuing = Block();
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-fragment void F() {
-  while(true) {
-    break;
-  }
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_LoopWithContinuing) {
-    Func("a_statement", {}, ty.void_(), tint::Empty);
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")));
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-#define TINT_ISOLATE_UB(VOLATILE_NAME) \
-  {volatile bool VOLATILE_NAME = false; if (VOLATILE_NAME) break;}
-
-void a_statement() {
-}
-
-fragment void F() {
-  while(true) {
-    TINT_ISOLATE_UB(tint_volatile_false);
-    break;
-    {
-      a_statement();
-    }
-  }
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_LoopWithContinuing_WithoutRobustness) {
-    Func("a_statement", {}, ty.void_(), tint::Empty);
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")));
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void a_statement() {
-}
-
-fragment void F() {
-  while(true) {
-    break;
-    {
-      a_statement();
-    }
-  }
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_LoopWithContinuing_BreakIf) {
-    Func("a_statement", {}, ty.void_(), {});
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-#define TINT_ISOLATE_UB(VOLATILE_NAME) \
-  {volatile bool VOLATILE_NAME = false; if (VOLATILE_NAME) break;}
-
-void a_statement() {
-}
-
-fragment void F() {
-  while(true) {
-    TINT_ISOLATE_UB(tint_volatile_false);
-    break;
-    {
-      a_statement();
-      if (true) { break; }
-    }
-  }
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_LoopWithContinuing_BreakIf_WithoutRobustness) {
-    Func("a_statement", {}, ty.void_(), {});
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void a_statement() {
-}
-
-fragment void F() {
-  while(true) {
-    break;
-    {
-      a_statement();
-      if (true) { break; }
-    }
-  }
-  return;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_LoopNestedWithContinuing) {
-    Func("a_statement", {}, ty.void_(), tint::Empty);
-
-    GlobalVar("lhs", ty.f32(), core::AddressSpace::kPrivate);
-    GlobalVar("rhs", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")));
-    auto* inner = Loop(body, continuing);
-
-    body = Block(inner);
-
-    continuing = Block(Assign("lhs", "rhs"), BreakIf(true));
-
-    auto* outer = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{outer}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  while(true) {
-    TINT_ISOLATE_UB(tint_volatile_false_1);
-    break;
-    {
-      a_statement();
-    }
-  }
-  {
-    lhs = rhs;
-    if (true) { break; }
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_LoopNestedWithContinuing_WithoutRobustness) {
-    Func("a_statement", {}, ty.void_(), tint::Empty);
-
-    GlobalVar("lhs", ty.f32(), core::AddressSpace::kPrivate);
-    GlobalVar("rhs", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")));
-    auto* inner = Loop(body, continuing);
-
-    body = Block(inner);
-
-    continuing = Block(Assign("lhs", "rhs"), BreakIf(true));
-
-    auto* outer = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{outer}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  while(true) {
-    break;
-    {
-      a_statement();
-    }
-  }
-  {
-    lhs = rhs;
-    if (true) { break; }
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_LoopWithVarUsedInContinuing) {
-    // loop {
-    //   var lhs : f32 = 2.5;
-    //   var other : f32;
-    //   continuing {
-    //     lhs = rhs
-    //   }
-    // }
-    //
-
-    GlobalVar("rhs", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.5_f))),  //
-                       Decl(Var("other", ty.f32())),             //
-                       Break());
-
-    auto* continuing = Block(Assign("lhs", "rhs"));
-    auto* outer = Loop(body, continuing);
-    WrapInFunction(outer);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  float lhs = 2.5f;
-  float other = 0.0f;
-  break;
-  {
-    lhs = rhs;
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_LoopWithVarUsedInContinuing_WithoutRobustness) {
-    // loop {
-    //   var lhs : f32 = 2.5;
-    //   var other : f32;
-    //   continuing {
-    //     lhs = rhs
-    //   }
-    // }
-    //
-
-    GlobalVar("rhs", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.5_f))),  //
-                       Decl(Var("other", ty.f32())),             //
-                       Break());
-
-    auto* continuing = Block(Assign("lhs", "rhs"));
-    auto* outer = Loop(body, continuing);
-    WrapInFunction(outer);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  float lhs = 2.5f;
-  float other = 0.0f;
-  break;
-  {
-    lhs = rhs;
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoop) {
-    // for(; ; ) {
-    //   return;
-    // }
-
-    auto* f = For(nullptr, nullptr, nullptr,  //
-                  Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(for(; ; ) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoop_WithoutRobustness) {
-    // for(; ; ) {
-    //   return;
-    // }
-
-    auto* f = For(nullptr, nullptr, nullptr,  //
-                  Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(for(; ; ) {
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithSimpleInit) {
-    // for(var i : i32; ; ) {
-    //   return;
-    // }
-
-    auto* f = For(Decl(Var("i", ty.i32())), nullptr, nullptr,  //
-                  Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(for(int i = 0; ; ) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithSimpleInit_WithoutRobustness) {
-    // for(var i : i32; ; ) {
-    //   return;
-    // }
-
-    auto* f = For(Decl(Var("i", ty.i32())), nullptr, nullptr,  //
-                  Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(for(int i = 0; ; ) {
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithMultiStmtInit) {
-    // fn f(i : i32) {}
-    //
-    // var<workgroup> a : atomic<i32>;
-    // for({f(1i); f(2i);}; ; ) {
-    //   return;
-    // }
-
-    Func("f", Vector{Param("i", ty.i32())}, ty.void_(), tint::Empty);
-    auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
-
-    GlobalVar("a", ty.atomic<i32>(), core::AddressSpace::kWorkgroup);
-    auto* multi_stmt = Block(f(1_i), f(2_i));
-    auto* loop = For(multi_stmt, nullptr, nullptr,  //
-                     Block(Return()));
-    WrapInFunction(loop);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"({
-  {
-    f(1);
-    f(2);
-  }
-  for(; ; ) {
-    TINT_ISOLATE_UB(tint_volatile_false);
-    return;
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithMultiStmtInit_WithoutRobustness) {
-    // fn f(i : i32) {}
-    //
-    // var<workgroup> a : atomic<i32>;
-    // for({f(1i); f(2i);}; ; ) {
-    //   return;
-    // }
-
-    Func("f", Vector{Param("i", ty.i32())}, ty.void_(), tint::Empty);
-    auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
-
-    GlobalVar("a", ty.atomic<i32>(), core::AddressSpace::kWorkgroup);
-    auto* multi_stmt = Block(f(1_i), f(2_i));
-    auto* loop = For(multi_stmt, nullptr, nullptr,  //
-                     Block(Return()));
-    WrapInFunction(loop);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"({
-  {
-    f(1);
-    f(2);
-  }
-  for(; ; ) {
-    return;
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithSimpleCond) {
-    // for(; true; ) {
-    //   return;
-    // }
-
-    auto* f = For(nullptr, true, nullptr,  //
-                  Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(for(; true; ) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithSimpleCond_WithoutRobustness) {
-    // for(; true; ) {
-    //   return;
-    // }
-
-    auto* f = For(nullptr, true, nullptr,  //
-                  Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(for(; true; ) {
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithSimpleCont) {
-    // for(; ; i = i + 1) {
-    //   return;
-    // }
-
-    auto* v = Decl(Var("i", ty.i32()));
-    auto* f = For(nullptr, nullptr, Assign("i", Add("i", 1_i)),  //
-                  Block(Return()));
-    WrapInFunction(v, f);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(for(; ; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithSimpleCont_WithoutRobustness) {
-    // for(; ; i = i + 1) {
-    //   return;
-    // }
-
-    auto* v = Decl(Var("i", ty.i32()));
-    auto* f = For(nullptr, nullptr, Assign("i", Add("i", 1_i)),  //
-                  Block(Return()));
-    WrapInFunction(v, f);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(for(; ; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithMultiStmtCont) {
-    // fn f(i : i32) {}
-    //
-    // var<workgroup> a : atomic<i32>;
-    // for(; ; { f(1i); f(2i); }) {
-    //   return;
-    // }
-
-    Func("f", Vector{Param("i", ty.i32())}, ty.void_(), tint::Empty);
-    auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
-
-    GlobalVar("a", ty.atomic<i32>(), core::AddressSpace::kWorkgroup);
-    auto* multi_stmt = Block(f(1_i), f(2_i));
-    auto* loop = For(nullptr, nullptr, multi_stmt,  //
-                     Block(Return()));
-    WrapInFunction(loop);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  return;
-  {
-    f(1);
-    f(2);
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithMultiStmtCont_WithoutRobustness) {
-    // fn f(i : i32) {}
-    //
-    // var<workgroup> a : atomic<i32>;
-    // for(; ; { f(1i); f(2i); }) {
-    //   return;
-    // }
-
-    Func("f", Vector{Param("i", ty.i32())}, ty.void_(), tint::Empty);
-    auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
-
-    GlobalVar("a", ty.atomic<i32>(), core::AddressSpace::kWorkgroup);
-    auto* multi_stmt = Block(f(1_i), f(2_i));
-    auto* loop = For(nullptr, nullptr, multi_stmt,  //
-                     Block(Return()));
-    WrapInFunction(loop);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  return;
-  {
-    f(1);
-    f(2);
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithSimpleInitCondCont) {
-    // for(var i : i32; true; i = i + 1) {
-    //   return;
-    // }
-
-    Func("a_statement", {}, ty.void_(), tint::Empty);
-
-    auto* f = For(Decl(Var("i", ty.i32())), true, Assign("i", Add("i", 1_i)),
-                  Block(CallStmt(Call("a_statement"))));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(for(int i = 0; true; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  a_statement();
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithSimpleInitCondCont_WithoutRobustness) {
-    // for(var i : i32; true; i = i + 1) {
-    //   return;
-    // }
-
-    Func("a_statement", {}, ty.void_(), tint::Empty);
-
-    auto* f = For(Decl(Var("i", ty.i32())), true, Assign("i", Add("i", 1_i)),
-                  Block(CallStmt(Call("a_statement"))));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(for(int i = 0; true; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
-  a_statement();
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithMultiStmtInitCondCont) {
-    // fn f(i : i32) {}
-    //
-    // var<workgroup> a : atomic<i32>;
-    // for({ f(1i); f(2i); }; true; { f(3i); f(4i); }) {
-    //   return;
-    // }
-
-    Func("f", Vector{Param("i", ty.i32())}, ty.void_(), tint::Empty);
-    auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
-
-    GlobalVar("a", ty.atomic<i32>(), core::AddressSpace::kWorkgroup);
-    auto* multi_stmt_a = Block(f(1_i), f(2_i));
-    auto* multi_stmt_b = Block(f(3_i), f(4_i));
-    auto* loop = For(multi_stmt_a, Expr(true), multi_stmt_b,  //
-                     Block(Return()));
-    WrapInFunction(loop);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"({
-  {
-    f(1);
-    f(2);
-  }
-  while(true) {
-    TINT_ISOLATE_UB(tint_volatile_false);
-    if (!(true)) { break; }
-    return;
-    {
-      f(3);
-      f(4);
-    }
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_ForLoopWithMultiStmtInitCondCont_WithoutRobustness) {
-    // fn f(i : i32) {}
-    //
-    // var<workgroup> a : atomic<i32>;
-    // for({ f(1i); f(2i); }; true; { f(3i); f(4i); }) {
-    //   return;
-    // }
-
-    Func("f", Vector{Param("i", ty.i32())}, ty.void_(), tint::Empty);
-    auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
-
-    GlobalVar("a", ty.atomic<i32>(), core::AddressSpace::kWorkgroup);
-    auto* multi_stmt_a = Block(f(1_i), f(2_i));
-    auto* multi_stmt_b = Block(f(3_i), f(4_i));
-    auto* loop = For(multi_stmt_a, Expr(true), multi_stmt_b,  //
-                     Block(Return()));
-    WrapInFunction(loop);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"({
-  {
-    f(1);
-    f(2);
-  }
-  while(true) {
-    if (!(true)) { break; }
-    return;
-    {
-      f(3);
-      f(4);
-    }
-  }
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_While) {
-    // while(true) {
-    //   return;
-    // }
-
-    auto* f = While(Expr(true), Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_While_WithoutRobustness) {
-    // while(true) {
-    //   return;
-    // }
-
-    auto* f = While(Expr(true), Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_While_WithContinue) {
-    // while(true) {
-    //   continue;
-    // }
-
-    auto* f = While(Expr(true), Block(Continue()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  continue;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_While_WithContinue_WithoutRobustness) {
-    // while(true) {
-    //   continue;
-    // }
-
-    auto* f = While(Expr(true), Block(Continue()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while(true) {
-  continue;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_WhileWithMultiCond) {
-    // while(true && false) {
-    //   return;
-    // }
-
-    auto* t = Let("t", Expr(true));
-    auto* multi_stmt = LogicalAnd(t, false);
-    // create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr(t), Expr(false));
-    auto* f = While(multi_stmt, Block(Return()));
-    WrapInFunction(t, f);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while((t && false)) {
-  TINT_ISOLATE_UB(tint_volatile_false);
-  return;
-}
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_WhileWithMultiCond_WithoutRobustness) {
-    // while(true && false) {
-    //   return;
-    // }
-
-    auto* t = Let("t", Expr(true));
-    auto* multi_stmt = LogicalAnd(t, false);
-    // create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr(t), Expr(false));
-    auto* f = While(multi_stmt, Block(Return()));
-    WrapInFunction(t, f);
-
-    ASTPrinter& gen = Build(NoRobustness());
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(while((t && false)) {
-  return;
-}
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/member_accessor_test.cc b/src/tint/lang/msl/writer/ast_printer/member_accessor_test.cc
deleted file mode 100644
index 18e3af1..0000000
--- a/src/tint/lang/msl/writer/ast_printer/member_accessor_test.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, EmitExpression_MemberAccessor) {
-    GlobalVar("str", ty.Of(Structure("my_str", Vector{Member("mem", ty.f32())})),
-              core::AddressSpace::kPrivate);
-    auto* expr = MemberAccessor("str", "mem");
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "str.mem");
-}
-
-TEST_F(MslASTPrinterTest, EmitExpression_MemberAccessor_Swizzle_xyz) {
-    GlobalVar("my_vec", ty.vec4<f32>(), core::AddressSpace::kPrivate);
-
-    auto* expr = MemberAccessor("my_vec", "xyz");
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "my_vec.xyz");
-}
-
-TEST_F(MslASTPrinterTest, EmitExpression_MemberAccessor_Swizzle_gbr) {
-    GlobalVar("my_vec", ty.vec4<f32>(), core::AddressSpace::kPrivate);
-
-    auto* expr = MemberAccessor("my_vec", "gbr");
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "my_vec.gbr");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/module_constant_test.cc b/src/tint/lang/msl/writer/ast_printer/module_constant_test.cc
deleted file mode 100644
index f357199..0000000
--- a/src/tint/lang/msl/writer/ast_printer/module_constant_test.cc
+++ /dev/null
@@ -1,344 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/id_attribute.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_AInt) {
-    auto* var = GlobalConst("G", Expr(1_a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  int const l = 1;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_AFloat) {
-    auto* var = GlobalConst("G", Expr(1._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float const l = 1.0f;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_i32) {
-    auto* var = GlobalConst("G", Expr(1_i));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  int const l = 1;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_u32) {
-    auto* var = GlobalConst("G", Expr(1_u));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  uint const l = 1u;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_f32) {
-    auto* var = GlobalConst("G", Expr(1_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float const l = 1.0f;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = GlobalConst("G", Expr(1_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  half const l = 1.0h;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_vec3_AInt) {
-    auto* var = GlobalConst("G", Call<vec3<Infer>>(1_a, 2_a, 3_a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  int3 const l = int3(1, 2, 3);
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_vec3_AFloat) {
-    auto* var = GlobalConst("G", Call<vec3<Infer>>(1._a, 2._a, 3._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float3 const l = float3(1.0f, 2.0f, 3.0f);
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_vec3_f32) {
-    auto* var = GlobalConst("G", Call<vec3<f32>>(1_f, 2_f, 3_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float3 const l = float3(1.0f, 2.0f, 3.0f);
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_vec3_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = GlobalConst("G", Call<vec3<f16>>(1_h, 2_h, 3_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  half3 const l = half3(1.0h, 2.0h, 3.0h);
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_mat2x3_AFloat) {
-    auto* var = GlobalConst("G", Call<mat2x3<Infer>>(1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_mat2x3_f32) {
-    auto* var = GlobalConst("G", Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_mat2x3_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = GlobalConst("G", Call<mat2x3<f16>>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  half2x3 const l = half2x3(half3(1.0h, 2.0h, 3.0h), half3(4.0h, 5.0h, 6.0h));
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_arr_f32) {
-    auto* var = GlobalConst("G", Call(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-void f() {
-  tint_array<float, 3> const l = tint_array<float, 3>{1.0f, 2.0f, 3.0f};
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_GlobalConst_arr_vec2_bool) {
-    auto* var = GlobalConst("G", Call<array<vec2<bool>, 3>>(Call<vec2<bool>>(true, false),  //
-                                                            Call<vec2<bool>>(false, true),  //
-                                                            Call<vec2<bool>>(true, true)));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-void f() {
-  tint_array<bool2, 3> const l = tint_array<bool2, 3>{bool2(true, false), bool2(false, true), bool2(true)};
-}
-
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/return_test.cc b/src/tint/lang/msl/writer/ast_printer/return_test.cc
deleted file mode 100644
index 4ea89a6..0000000
--- a/src/tint/lang/msl/writer/ast_printer/return_test.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-// All ported to IR tests
-
-using MslASTPrinterTest = TestHelper;
-
-// MslPrinterTest.Return
-TEST_F(MslASTPrinterTest, Emit_Return) {
-    auto* r = Return();
-    WrapInFunction(r);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  return;\n");
-}
-
-// MslPrinterTest.ReturnWithValue
-TEST_F(MslASTPrinterTest, Emit_ReturnWithValue) {
-    auto* r = Return(123_i);
-    Func("f", tint::Empty, ty.i32(), Vector{r});
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  return 123;\n");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/sanitizer_test.cc b/src/tint/lang/msl/writer/ast_printer/sanitizer_test.cc
deleted file mode 100644
index 901ddd2..0000000
--- a/src/tint/lang/msl/writer/ast_printer/sanitizer_test.cc
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using ::testing::HasSubstr;
-using MslSanitizerTest = TestHelper;
-
-TEST_F(MslSanitizerTest, Call_ArrayLength) {
-    auto* s = Structure("my_struct", Vector{Member(0, "a", ty.array<f32>())});
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(2_a));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("len", ty.u32(), Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    Options opts = DefaultOptions();
-    opts.array_length_from_uniform.ubo_binding = 30;
-    opts.array_length_from_uniform.bindpoint_to_size_index.emplace(BindingPoint{2, 1}, 1);
-    ASTPrinter& gen = SanitizeAndBuild(opts);
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-struct TintArrayLengths {
-  /* 0x0000 */ tint_array<uint4, 1> array_lengths;
-};
-
-struct my_struct {
-  tint_array<float, 1> a;
-};
-
-fragment void a_func(const constant TintArrayLengths* tint_symbol [[buffer(30)]]) {
-  uint len = (((*(tint_symbol)).array_lengths[0u][1u] - 0u) / 4u);
-  return;
-}
-
-)";
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(MslSanitizerTest, Call_ArrayLength_OtherMembersInStruct) {
-    auto* s = Structure("my_struct", Vector{
-                                         Member(0, "z", ty.f32()),
-                                         Member(4, "a", ty.array<f32>()),
-                                     });
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(2_a));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("len", ty.u32(), Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    Options opts = DefaultOptions();
-    opts.array_length_from_uniform.ubo_binding = 30;
-    opts.array_length_from_uniform.bindpoint_to_size_index.emplace(BindingPoint{2, 1}, 1);
-    ASTPrinter& gen = SanitizeAndBuild(opts);
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-struct TintArrayLengths {
-  /* 0x0000 */ tint_array<uint4, 1> array_lengths;
-};
-
-struct my_struct {
-  float z;
-  tint_array<float, 1> a;
-};
-
-fragment void a_func(const constant TintArrayLengths* tint_symbol [[buffer(30)]]) {
-  uint len = (((*(tint_symbol)).array_lengths[0u][1u] - 4u) / 4u);
-  return;
-}
-
-)";
-
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(MslSanitizerTest, Call_ArrayLength_ViaLets) {
-    auto* s = Structure("my_struct", Vector{Member(0, "a", ty.array<f32>())});
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(2_a));
-
-    auto* p = Let("p", AddressOf("b"));
-    auto* p2 = Let("p2", AddressOf(MemberAccessor(Deref(p), "a")));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(p),
-             Decl(p2),
-             Decl(Var("len", ty.u32(), Call("arrayLength", p2))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    Options opts = DefaultOptions();
-    opts.array_length_from_uniform.ubo_binding = 30;
-    opts.array_length_from_uniform.bindpoint_to_size_index.emplace(BindingPoint{2, 1}, 1);
-    ASTPrinter& gen = SanitizeAndBuild(opts);
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-struct TintArrayLengths {
-  /* 0x0000 */ tint_array<uint4, 1> array_lengths;
-};
-
-struct my_struct {
-  tint_array<float, 1> a;
-};
-
-fragment void a_func(const constant TintArrayLengths* tint_symbol [[buffer(30)]]) {
-  uint len = (((*(tint_symbol)).array_lengths[0u][1u] - 0u) / 4u);
-  return;
-}
-
-)";
-
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(MslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) {
-    auto* s = Structure("my_struct", Vector{Member(0, "a", ty.array<f32>())});
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(0_a));
-    GlobalVar("c", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(2_a),
-              Group(0_a));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("len", ty.u32(),
-                      Add(Call("arrayLength", AddressOf(MemberAccessor("b", "a"))),
-                          Call("arrayLength", AddressOf(MemberAccessor("c", "a")))))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    Options options;
-    options.array_length_from_uniform.ubo_binding = 29;
-    options.array_length_from_uniform.bindpoint_to_size_index.emplace(BindingPoint{0, 1}, 7u);
-    options.array_length_from_uniform.bindpoint_to_size_index.emplace(BindingPoint{0, 2}, 2u);
-    ASTPrinter& gen = SanitizeAndBuild(options);
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-struct TintArrayLengths {
-  /* 0x0000 */ tint_array<uint4, 2> array_lengths;
-};
-
-struct my_struct {
-  tint_array<float, 1> a;
-};
-
-fragment void a_func(const constant TintArrayLengths* tint_symbol [[buffer(29)]]) {
-  uint len = ((((*(tint_symbol)).array_lengths[1u][3u] - 0u) / 4u) + (((*(tint_symbol)).array_lengths[0u][2u] - 0u) / 4u));
-  return;
-}
-
-)";
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(MslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniformMissingBinding) {
-    auto* s = Structure("my_struct", Vector{Member(0, "a", ty.array<f32>())});
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(0_a));
-    GlobalVar("c", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(2_a),
-              Group(0_a));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("len", ty.u32(),
-                      Add(Call("arrayLength", AddressOf(MemberAccessor("b", "a"))),
-                          Call("arrayLength", AddressOf(MemberAccessor("c", "a")))))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    Options options;
-    options.array_length_from_uniform.ubo_binding = 29;
-    options.array_length_from_uniform.bindpoint_to_size_index.emplace(BindingPoint{0, 2}, 2u);
-    ASTPrinter& gen = SanitizeAndBuild(options);
-
-    ASSERT_FALSE(gen.Generate());
-    EXPECT_THAT(gen.Diagnostics().Str(), HasSubstr("Unable to translate builtin: arrayLength"));
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/switch_test.cc b/src/tint/lang/msl/writer/ast_printer/switch_test.cc
deleted file mode 100644
index 78a74f4..0000000
--- a/src/tint/lang/msl/writer/ast_printer/switch_test.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_Switch) {
-    auto* cond = Var("cond", ty.i32());
-
-    auto* def_body = Block(create<ast::BreakStatement>());
-    auto* def = Case(DefaultCaseSelector(), def_body);
-
-    auto* case_body = Block(create<ast::BreakStatement>());
-    auto* case_stmt = Case(CaseSelector(5_i), case_body);
-
-    Vector body{case_stmt, def};
-    auto* s = Switch(cond, body);
-    WrapInFunction(cond, s);
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  switch(cond) {
-    case 5: {
-      break;
-    }
-    default: {
-      break;
-    }
-  }
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_Switch_MixedDefault) {
-    auto* cond = Var("cond", ty.i32());
-
-    auto* def_body = Block(create<ast::BreakStatement>());
-    auto* def = Case(Vector{CaseSelector(5_i), DefaultCaseSelector()}, def_body);
-
-    auto* s = Switch(cond, def);
-    WrapInFunction(cond, s);
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  switch(cond) {
-    case 5:
-    default: {
-      break;
-    }
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/unary_op_test.cc b/src/tint/lang/msl/writer/ast_printer/unary_op_test.cc
deleted file mode 100644
index 629653b..0000000
--- a/src/tint/lang/msl/writer/ast_printer/unary_op_test.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using MslUnaryOpTest = TestHelper;
-
-TEST_F(MslUnaryOpTest, AddressOf) {
-    GlobalVar("expr", ty.f32(), core::AddressSpace::kPrivate);
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kAddressOf, Expr("expr"));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "&(expr)");
-}
-
-TEST_F(MslUnaryOpTest, Complement) {
-    GlobalVar("expr", ty.i32(), core::AddressSpace::kPrivate);
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kComplement, Expr("expr"));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "~(expr)");
-}
-
-TEST_F(MslUnaryOpTest, Indirection) {
-    GlobalVar("G", ty.f32(), core::AddressSpace::kPrivate);
-    auto* p = Let("expr", create<ast::UnaryOpExpression>(core::UnaryOp::kAddressOf, Expr("G")));
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kIndirection, Expr("expr"));
-    WrapInFunction(p, op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "*(expr)");
-}
-
-TEST_F(MslUnaryOpTest, Not) {
-    GlobalVar("expr", ty.bool_(), core::AddressSpace::kPrivate);
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kNot, Expr("expr"));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "!(expr)");
-}
-
-TEST_F(MslUnaryOpTest, Negation) {
-    GlobalVar("expr", ty.i32(), core::AddressSpace::kPrivate);
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kNegation, Expr("expr"));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "tint_unary_minus(expr)");
-}
-
-TEST_F(MslUnaryOpTest, IntMin) {
-    auto* op = Expr(i32(std::numeric_limits<int32_t>::min()));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(-2147483647 - 1)");
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_printer/variable_decl_statement_test.cc b/src/tint/lang/msl/writer/ast_printer/variable_decl_statement_test.cc
deleted file mode 100644
index ba3a4f2..0000000
--- a/src/tint/lang/msl/writer/ast_printer/variable_decl_statement_test.cc
+++ /dev/null
@@ -1,563 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-#include "gmock/gmock.h"
-#include "src/tint/lang/msl/writer/ast_printer/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using ::testing::HasSubstr;
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using MslASTPrinterTest = TestHelper;
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement) {
-    auto* var = Var("a", ty.f32());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  float a = 0.0f;\n");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Let) {
-    auto* var = Let("a", ty.f32(), Call<f32>());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  float const a = 0.0f;\n");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const) {
-    auto* var = Const("a", ty.f32(), Call<f32>());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "");  // Not a mistake - 'const' is inlined
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_AInt) {
-    auto* C = Const("C", Expr(1_a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  int const l = 1;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_AFloat) {
-    auto* C = Const("C", Expr(1._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float const l = 1.0f;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_i32) {
-    auto* C = Const("C", Expr(1_i));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  int const l = 1;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_u32) {
-    auto* C = Const("C", Expr(1_u));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  uint const l = 1u;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_f32) {
-    auto* C = Const("C", Expr(1_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float const l = 1.0f;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* C = Const("C", Expr(1_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  half const l = 1.0h;
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_vec3_AInt) {
-    auto* C = Const("C", Call<vec3<Infer>>(1_a, 2_a, 3_a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  int3 const l = int3(1, 2, 3);
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_vec3_AFloat) {
-    auto* C = Const("C", Call<vec3<Infer>>(1._a, 2._a, 3._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float3 const l = float3(1.0f, 2.0f, 3.0f);
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_vec3_f32) {
-    auto* C = Const("C", Call<vec3<f32>>(1_f, 2_f, 3_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float3 const l = float3(1.0f, 2.0f, 3.0f);
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_vec3_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* C = Const("C", Call<vec3<f16>>(1_h, 2_h, 3_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  half3 const l = half3(1.0h, 2.0h, 3.0h);
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
-    auto* C = Const("C", Call<mat2x3<Infer>>(1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_mat2x3_f32) {
-    auto* C = Const("C", Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_mat2x3_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* C = Const("C", Call<mat2x3<f16>>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-void f() {
-  half2x3 const l = half2x3(half3(1.0h, 2.0h, 3.0h), half3(4.0h, 5.0h, 6.0h));
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_arr_f32) {
-    auto* C = Const("C", Call<array<f32, 3>>(1_f, 2_f, 3_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-void f() {
-  tint_array<float, 3> const l = tint_array<float, 3>{1.0f, 2.0f, 3.0f};
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
-    auto* C = Const("C", Call<array<vec2<bool>, 3>>(         //
-                             Call<vec2<bool>>(true, false),  //
-                             Call<vec2<bool>>(false, true),  //
-                             Call<vec2<bool>>(true, true)));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(C), Decl(Let("l", Expr(C)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(#include <metal_stdlib>
-
-using namespace metal;
-
-template<typename T, size_t N>
-struct tint_array {
-    const constant T& operator[](size_t i) const constant { return elements[i]; }
-    device T& operator[](size_t i) device { return elements[i]; }
-    const device T& operator[](size_t i) const device { return elements[i]; }
-    thread T& operator[](size_t i) thread { return elements[i]; }
-    const thread T& operator[](size_t i) const thread { return elements[i]; }
-    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
-    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
-    T elements[N];
-};
-
-void f() {
-  tint_array<bool2, 3> const l = tint_array<bool2, 3>{bool2(true, false), bool2(false, true), bool2(true)};
-}
-
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Array) {
-    auto* var = Var("a", ty.array<f32, 5>());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  tint_array<float, 5> a = {};\n");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Struct) {
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.f32()),
-                                 Member("b", ty.f32()),
-                             });
-
-    auto* var = Var("a", ty.Of(s));
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  S a = {};
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Vector_f32) {
-    auto* var = Var("a", ty.vec2<f32>());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  float2 a = 0.0f;\n");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = Var("a", ty.vec2<f16>());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  half2 a = 0.0h;\n");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Matrix_f32) {
-    auto* var = Var("a", ty.mat3x2<f32>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  float3x2 a = float3x2(0.0f);\n");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Matrix_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = Var("a", ty.mat3x2<f16>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  half3x2 a = half3x2(0.0h);\n");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Initializer_ZeroVec_f32) {
-    auto* var = Var("a", ty.vec3<f32>(), Call<vec3<f32>>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float3 a = float3(0.0f);
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Initializer_ZeroVec_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = Var("a", ty.vec3<f16>(), Call<vec3<f16>>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(half3 a = half3(0.0h);
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Initializer_ZeroMat_f32) {
-    auto* var = Var("a", ty.mat2x3<f32>(), Call<mat2x3<f32>>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(float2x3 a = float2x3(float3(0.0f), float3(0.0f));
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Initializer_ZeroMat_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = Var("a", ty.mat2x3<f16>(), Call<mat2x3<f16>>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(half2x3 a = half2x3(half3(0.0h), half3(0.0h));
-)");
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Private) {
-    GlobalVar("a", ty.f32(), core::AddressSpace::kPrivate);
-
-    WrapInFunction(Expr("a"));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(thread tint_private_vars_struct tint_private_vars = {};
-    float const tint_symbol = tint_private_vars.a;
-    return;
-)"));
-}
-
-TEST_F(MslASTPrinterTest, Emit_VariableDeclStatement_Workgroup) {
-    GlobalVar("a", ty.f32(), core::AddressSpace::kWorkgroup);
-
-    WrapInFunction(Expr("a"));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("threadgroup float tint_symbol_"));
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/BUILD.bazel b/src/tint/lang/msl/writer/ast_raise/BUILD.bazel
deleted file mode 100644
index f832b43..0000000
--- a/src/tint/lang/msl/writer/ast_raise/BUILD.bazel
+++ /dev/null
@@ -1,177 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.bazel.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-load("//src/tint:flags.bzl", "COPTS")
-load("@bazel_skylib//lib:selects.bzl", "selects")
-cc_library(
-  name = "ast_raise",
-  srcs = [
-    "module_scope_var_to_entry_point_param.cc",
-    "packed_vec3.cc",
-    "pixel_local.cc",
-    "quad_swap.cc",
-    "subgroup_ballot.cc",
-  ],
-  hdrs = [
-    "module_scope_var_to_entry_point_param.h",
-    "packed_vec3.h",
-    "pixel_local.h",
-    "quad_swap.h",
-    "subgroup_ballot.h",
-  ],
-  deps = [
-    "//src/tint/api/common",
-    "//src/tint/lang/core",
-    "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/type",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast/transform",
-    "//src/tint/lang/wgsl/common",
-    "//src/tint/lang/wgsl/features",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/resolver",
-    "//src/tint/lang/wgsl/sem",
-    "//src/tint/utils",
-    "//src/tint/utils/containers",
-    "//src/tint/utils/diagnostic",
-    "//src/tint/utils/ice",
-    "//src/tint/utils/macros",
-    "//src/tint/utils/math",
-    "//src/tint/utils/memory",
-    "//src/tint/utils/result",
-    "//src/tint/utils/rtti",
-    "//src/tint/utils/symbol",
-    "//src/tint/utils/text",
-    "//src/utils",
-  ],
-  copts = COPTS,
-  visibility = ["//visibility:public"],
-)
-cc_library(
-  name = "test",
-  alwayslink = True,
-  srcs = [
-    "module_scope_var_to_entry_point_param_test.cc",
-    "packed_vec3_test.cc",
-    "pixel_local_test.cc",
-    "quad_swap_test.cc",
-    "subgroup_ballot_test.cc",
-  ],
-  deps = [
-    "//src/tint/api/common",
-    "//src/tint/lang/core",
-    "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/ir",
-    "//src/tint/lang/core/type",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast/transform",
-    "//src/tint/lang/wgsl/common",
-    "//src/tint/lang/wgsl/features",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/resolver",
-    "//src/tint/lang/wgsl/sem",
-    "//src/tint/lang/wgsl/writer/ir_to_program",
-    "//src/tint/utils",
-    "//src/tint/utils/containers",
-    "//src/tint/utils/diagnostic",
-    "//src/tint/utils/ice",
-    "//src/tint/utils/macros",
-    "//src/tint/utils/math",
-    "//src/tint/utils/memory",
-    "//src/tint/utils/result",
-    "//src/tint/utils/rtti",
-    "//src/tint/utils/symbol",
-    "//src/tint/utils/text",
-    "@gtest",
-    "//src/utils",
-  ] + select({
-    ":tint_build_msl_writer": [
-      "//src/tint/lang/msl/writer/ast_raise",
-    ],
-    "//conditions:default": [],
-  }) + select({
-    ":tint_build_wgsl_reader": [
-      "//src/tint/lang/wgsl/reader",
-    ],
-    "//conditions:default": [],
-  }) + select({
-    ":tint_build_wgsl_reader_and_tint_build_wgsl_writer": [
-      "//src/tint/lang/wgsl/ast/transform:test",
-    ],
-    "//conditions:default": [],
-  }) + select({
-    ":tint_build_wgsl_writer": [
-      "//src/tint/lang/wgsl/writer",
-    ],
-    "//conditions:default": [],
-  }),
-  copts = COPTS,
-  visibility = ["//visibility:public"],
-)
-
-alias(
-  name = "tint_build_msl_writer",
-  actual = "//src/tint:tint_build_msl_writer_true",
-)
-
-alias(
-  name = "tint_build_wgsl_reader",
-  actual = "//src/tint:tint_build_wgsl_reader_true",
-)
-
-alias(
-  name = "tint_build_wgsl_writer",
-  actual = "//src/tint:tint_build_wgsl_writer_true",
-)
-
-selects.config_setting_group(
-    name = "tint_build_msl_writer_and_tint_build_wgsl_reader_and_tint_build_wgsl_writer",
-    match_all = [
-        ":tint_build_msl_writer",
-        ":tint_build_wgsl_reader",
-        ":tint_build_wgsl_writer",
-    ],
-)
-selects.config_setting_group(
-    name = "tint_build_wgsl_reader_and_tint_build_wgsl_writer",
-    match_all = [
-        ":tint_build_wgsl_reader",
-        ":tint_build_wgsl_writer",
-    ],
-)
-
diff --git a/src/tint/lang/msl/writer/ast_raise/BUILD.cfg b/src/tint/lang/msl/writer/ast_raise/BUILD.cfg
deleted file mode 100644
index 98cff52..0000000
--- a/src/tint/lang/msl/writer/ast_raise/BUILD.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-    "condition": "tint_build_msl_writer",
-    "test": {
-        "condition": "tint_build_wgsl_reader && tint_build_wgsl_writer",
-    }
-}
diff --git a/src/tint/lang/msl/writer/ast_raise/BUILD.cmake b/src/tint/lang/msl/writer/ast_raise/BUILD.cmake
deleted file mode 100644
index 266c257..0000000
--- a/src/tint/lang/msl/writer/ast_raise/BUILD.cmake
+++ /dev/null
@@ -1,158 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.cmake.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-if(TINT_BUILD_MSL_WRITER)
-################################################################################
-# Target:    tint_lang_msl_writer_ast_raise
-# Kind:      lib
-# Condition: TINT_BUILD_MSL_WRITER
-################################################################################
-tint_add_target(tint_lang_msl_writer_ast_raise lib
-  lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.cc
-  lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.h
-  lang/msl/writer/ast_raise/packed_vec3.cc
-  lang/msl/writer/ast_raise/packed_vec3.h
-  lang/msl/writer/ast_raise/pixel_local.cc
-  lang/msl/writer/ast_raise/pixel_local.h
-  lang/msl/writer/ast_raise/quad_swap.cc
-  lang/msl/writer/ast_raise/quad_swap.h
-  lang/msl/writer/ast_raise/subgroup_ballot.cc
-  lang/msl/writer/ast_raise/subgroup_ballot.h
-)
-
-tint_target_add_dependencies(tint_lang_msl_writer_ast_raise lib
-  tint_api_common
-  tint_lang_core
-  tint_lang_core_constant
-  tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_common
-  tint_lang_wgsl_features
-  tint_lang_wgsl_program
-  tint_lang_wgsl_resolver
-  tint_lang_wgsl_sem
-  tint_utils
-  tint_utils_containers
-  tint_utils_diagnostic
-  tint_utils_ice
-  tint_utils_macros
-  tint_utils_math
-  tint_utils_memory
-  tint_utils_result
-  tint_utils_rtti
-  tint_utils_symbol
-  tint_utils_text
-)
-
-tint_target_add_external_dependencies(tint_lang_msl_writer_ast_raise lib
-  "src_utils"
-)
-
-endif(TINT_BUILD_MSL_WRITER)
-if(TINT_BUILD_MSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-################################################################################
-# Target:    tint_lang_msl_writer_ast_raise_test
-# Kind:      test
-# Condition: TINT_BUILD_MSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER
-################################################################################
-tint_add_target(tint_lang_msl_writer_ast_raise_test test
-  lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param_test.cc
-  lang/msl/writer/ast_raise/packed_vec3_test.cc
-  lang/msl/writer/ast_raise/pixel_local_test.cc
-  lang/msl/writer/ast_raise/quad_swap_test.cc
-  lang/msl/writer/ast_raise/subgroup_ballot_test.cc
-)
-
-tint_target_add_dependencies(tint_lang_msl_writer_ast_raise_test test
-  tint_api_common
-  tint_lang_core
-  tint_lang_core_constant
-  tint_lang_core_ir
-  tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_common
-  tint_lang_wgsl_features
-  tint_lang_wgsl_program
-  tint_lang_wgsl_resolver
-  tint_lang_wgsl_sem
-  tint_lang_wgsl_writer_ir_to_program
-  tint_utils
-  tint_utils_containers
-  tint_utils_diagnostic
-  tint_utils_ice
-  tint_utils_macros
-  tint_utils_math
-  tint_utils_memory
-  tint_utils_result
-  tint_utils_rtti
-  tint_utils_symbol
-  tint_utils_text
-)
-
-tint_target_add_external_dependencies(tint_lang_msl_writer_ast_raise_test test
-  "gtest"
-  "src_utils"
-)
-
-if(TINT_BUILD_MSL_WRITER)
-  tint_target_add_dependencies(tint_lang_msl_writer_ast_raise_test test
-    tint_lang_msl_writer_ast_raise
-  )
-endif(TINT_BUILD_MSL_WRITER)
-
-if(TINT_BUILD_WGSL_READER)
-  tint_target_add_dependencies(tint_lang_msl_writer_ast_raise_test test
-    tint_lang_wgsl_reader
-  )
-endif(TINT_BUILD_WGSL_READER)
-
-if(TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-  tint_target_add_dependencies(tint_lang_msl_writer_ast_raise_test test
-    tint_lang_wgsl_ast_transform_test
-  )
-endif(TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-
-if(TINT_BUILD_WGSL_WRITER)
-  tint_target_add_dependencies(tint_lang_msl_writer_ast_raise_test test
-    tint_lang_wgsl_writer
-  )
-endif(TINT_BUILD_WGSL_WRITER)
-
-endif(TINT_BUILD_MSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
\ No newline at end of file
diff --git a/src/tint/lang/msl/writer/ast_raise/BUILD.gn b/src/tint/lang/msl/writer/ast_raise/BUILD.gn
deleted file mode 100644
index 6be3773..0000000
--- a/src/tint/lang/msl/writer/ast_raise/BUILD.gn
+++ /dev/null
@@ -1,145 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.gn.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-import("../../../../../../scripts/dawn_overrides_with_defaults.gni")
-import("../../../../../../scripts/tint_overrides_with_defaults.gni")
-
-import("${tint_src_dir}/tint.gni")
-
-if (tint_build_unittests || tint_build_benchmarks) {
-  import("//testing/test.gni")
-}
-if (tint_build_msl_writer) {
-  libtint_source_set("ast_raise") {
-    sources = [
-      "module_scope_var_to_entry_point_param.cc",
-      "module_scope_var_to_entry_point_param.h",
-      "packed_vec3.cc",
-      "packed_vec3.h",
-      "pixel_local.cc",
-      "pixel_local.h",
-      "quad_swap.cc",
-      "quad_swap.h",
-      "subgroup_ballot.cc",
-      "subgroup_ballot.h",
-    ]
-    deps = [
-      "${dawn_root}/src/utils:utils",
-      "${tint_src_dir}/api/common",
-      "${tint_src_dir}/lang/core",
-      "${tint_src_dir}/lang/core/constant",
-      "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/wgsl",
-      "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/ast/transform",
-      "${tint_src_dir}/lang/wgsl/common",
-      "${tint_src_dir}/lang/wgsl/features",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/resolver",
-      "${tint_src_dir}/lang/wgsl/sem",
-      "${tint_src_dir}/utils",
-      "${tint_src_dir}/utils/containers",
-      "${tint_src_dir}/utils/diagnostic",
-      "${tint_src_dir}/utils/ice",
-      "${tint_src_dir}/utils/macros",
-      "${tint_src_dir}/utils/math",
-      "${tint_src_dir}/utils/memory",
-      "${tint_src_dir}/utils/result",
-      "${tint_src_dir}/utils/rtti",
-      "${tint_src_dir}/utils/symbol",
-      "${tint_src_dir}/utils/text",
-    ]
-  }
-}
-if (tint_build_unittests) {
-  if (tint_build_msl_writer && tint_build_wgsl_reader &&
-      tint_build_wgsl_writer) {
-    tint_unittests_source_set("unittests") {
-      sources = [
-        "module_scope_var_to_entry_point_param_test.cc",
-        "packed_vec3_test.cc",
-        "pixel_local_test.cc",
-        "quad_swap_test.cc",
-        "subgroup_ballot_test.cc",
-      ]
-      deps = [
-        "${dawn_root}/src/utils:utils",
-        "${tint_src_dir}:gmock_and_gtest",
-        "${tint_src_dir}/api/common",
-        "${tint_src_dir}/lang/core",
-        "${tint_src_dir}/lang/core/constant",
-        "${tint_src_dir}/lang/core/ir",
-        "${tint_src_dir}/lang/core/type",
-        "${tint_src_dir}/lang/wgsl",
-        "${tint_src_dir}/lang/wgsl/ast",
-        "${tint_src_dir}/lang/wgsl/ast/transform",
-        "${tint_src_dir}/lang/wgsl/common",
-        "${tint_src_dir}/lang/wgsl/features",
-        "${tint_src_dir}/lang/wgsl/program",
-        "${tint_src_dir}/lang/wgsl/resolver",
-        "${tint_src_dir}/lang/wgsl/sem",
-        "${tint_src_dir}/lang/wgsl/writer/ir_to_program",
-        "${tint_src_dir}/utils",
-        "${tint_src_dir}/utils/containers",
-        "${tint_src_dir}/utils/diagnostic",
-        "${tint_src_dir}/utils/ice",
-        "${tint_src_dir}/utils/macros",
-        "${tint_src_dir}/utils/math",
-        "${tint_src_dir}/utils/memory",
-        "${tint_src_dir}/utils/result",
-        "${tint_src_dir}/utils/rtti",
-        "${tint_src_dir}/utils/symbol",
-        "${tint_src_dir}/utils/text",
-      ]
-
-      if (tint_build_msl_writer) {
-        deps += [ "${tint_src_dir}/lang/msl/writer/ast_raise" ]
-      }
-
-      if (tint_build_wgsl_reader) {
-        deps += [ "${tint_src_dir}/lang/wgsl/reader" ]
-      }
-
-      if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
-        deps += [ "${tint_src_dir}/lang/wgsl/ast/transform:unittests" ]
-      }
-
-      if (tint_build_wgsl_writer) {
-        deps += [ "${tint_src_dir}/lang/wgsl/writer" ]
-      }
-    }
-  }
-}
diff --git a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.cc b/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.cc
deleted file mode 100644
index c1bde6c..0000000
--- a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.cc
+++ /dev/null
@@ -1,612 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.h"
-
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/disable_validation_attribute.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/module.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/text/string.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::ModuleScopeVarToEntryPointParam);
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using StructMemberList = tint::Vector<const ast::StructMember*, 8>;
-
-// The name of the struct member for arrays that are wrapped in structures.
-const char* kWrappedArrayMemberName = "arr";
-
-bool ShouldRun(const Program& program) {
-    for (auto* decl : program.AST().GlobalDeclarations()) {
-        if (decl->Is<ast::Variable>()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-// Returns `true` if `type` is or contains a matrix type.
-bool ContainsMatrix(const core::type::Type* type) {
-    type = type->UnwrapRef();
-    if (type->Is<core::type::Matrix>()) {
-        return true;
-    } else if (auto* ary = type->As<core::type::Array>()) {
-        return ContainsMatrix(ary->ElemType());
-    } else if (auto* str = type->As<core::type::Struct>()) {
-        for (auto* member : str->Members()) {
-            if (ContainsMatrix(member->Type())) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-}  // namespace
-
-/// PIMPL state for the transform
-struct ModuleScopeVarToEntryPointParam::State {
-    /// The clone context.
-    program::CloneContext& ctx;
-
-    /// Constructor
-    /// @param context the clone context
-    explicit State(program::CloneContext& context) : ctx(context) {}
-
-    /// Clone any struct types that are contained in `ty` (including `ty` itself),
-    /// and add it to the global declarations now, so that they precede new global
-    /// declarations that need to reference them.
-    /// @param ty the type to clone
-    void CloneStructTypes(const core::type::Type* ty) {
-        if (auto* str = ty->As<sem::Struct>()) {
-            if (!cloned_structs_.emplace(str).second) {
-                // The struct has already been cloned.
-                return;
-            }
-
-            // Recurse into members.
-            for (auto* member : str->Members()) {
-                CloneStructTypes(member->Type());
-            }
-
-            // Clone the struct and add it to the global declaration list.
-            // Remove the old declaration.
-            auto* ast_str = str->Declaration();
-            ctx.dst->AST().AddTypeDecl(ctx.Clone(ast_str));
-            ctx.Remove(ctx.src->AST().GlobalDeclarations(), ast_str);
-        } else if (auto* arr = ty->As<core::type::Array>()) {
-            CloneStructTypes(arr->ElemType());
-        }
-    }
-
-    /// Process a variable `var` that is referenced in the entry point function `func`.
-    /// This will redeclare the variable as a function parameter, possibly as a pointer.
-    /// Some workgroup variables will be redeclared as a member inside a workgroup structure.
-    /// @param func the entry point function
-    /// @param var the variable
-    /// @param new_var_symbol the symbol to use for the replacement
-    /// @param workgroup_param helper function to get a symbol to a workgroup struct parameter
-    /// @param workgroup_parameter_members reference to a list of a workgroup struct members
-    /// @param is_pointer output signalling whether the replacement is a pointer
-    /// @param is_wrapped output signalling whether the replacement is wrapped in a struct
-    void ProcessVariableInEntryPoint(const ast::Function* func,
-                                     const sem::Variable* var,
-                                     Symbol new_var_symbol,
-                                     std::function<Symbol()> workgroup_param,
-                                     StructMemberList& workgroup_parameter_members,
-                                     bool& is_pointer,
-                                     bool& is_wrapped) {
-        auto* ty = var->Type()->UnwrapRef();
-
-        // Helper to create an AST node for the store type of the variable.
-        auto store_type = [&] { return CreateASTTypeFor(ctx, ty); };
-
-        core::AddressSpace sc = var->AddressSpace();
-        switch (sc) {
-            case core::AddressSpace::kHandle: {
-                // For a texture or sampler variable, redeclare it as an entry point parameter.
-                // Disable entry point parameter validation.
-                auto* disable_validation =
-                    ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter);
-                auto attrs = ctx.Clone(var->Declaration()->attributes);
-                attrs.Push(disable_validation);
-                auto* param = ctx.dst->Param(new_var_symbol, store_type(), attrs);
-                ctx.InsertFront(func->params, param);
-
-                break;
-            }
-            case core::AddressSpace::kStorage:
-            case core::AddressSpace::kUniform: {
-                // Variables into the Storage and Uniform address spaces are redeclared as entry
-                // point parameters with a pointer type.
-                auto attributes = ctx.Clone(var->Declaration()->attributes);
-                attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter));
-                attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
-
-                auto param_type = store_type();
-                if (auto* arr = ty->As<core::type::Array>();
-                    arr && arr->Count()->Is<core::type::RuntimeArrayCount>()) {
-                    // Wrap runtime-sized arrays in structures, so that we can declare pointers to
-                    // them. Ideally we'd just emit the array itself as a pointer, but this is not
-                    // representable in Tint's AST.
-                    CloneStructTypes(ty);
-                    auto* wrapper = ctx.dst->Structure(
-                        ctx.dst->Sym(), tint::Vector{
-                                            ctx.dst->Member(kWrappedArrayMemberName, param_type),
-                                        });
-                    param_type = ctx.dst->ty.Of(wrapper);
-                    is_wrapped = true;
-                }
-
-                param_type = sc == core::AddressSpace::kStorage
-                                 ? ctx.dst->ty.ptr(sc, param_type, var->Access())
-                                 : ctx.dst->ty.ptr(sc, param_type);
-                auto* param = ctx.dst->Param(new_var_symbol, param_type, attributes);
-                ctx.InsertFront(func->params, param);
-                is_pointer = true;
-
-                break;
-            }
-            case core::AddressSpace::kWorkgroup: {
-                if (ContainsMatrix(var->Type())) {
-                    // Due to a bug in the MSL compiler, we use a threadgroup memory argument for
-                    // any workgroup allocation that contains a matrix. See crbug.com/tint/938.
-                    // TODO(jrprice): Do this for all other workgroup variables too.
-
-                    // Create a member in the workgroup parameter struct.
-                    auto member = ctx.Clone(var->Declaration()->name->symbol);
-                    workgroup_parameter_members.Push(ctx.dst->Member(member, store_type()));
-                    CloneStructTypes(var->Type()->UnwrapRef());
-
-                    // Create a function-scope variable that is a pointer to the member.
-                    auto* member_ptr = ctx.dst->AddressOf(
-                        ctx.dst->MemberAccessor(ctx.dst->Deref(workgroup_param()), member));
-                    auto* local_var = ctx.dst->Let(new_var_symbol, member_ptr);
-                    ctx.InsertFront(func->body->statements, ctx.dst->Decl(local_var));
-                    is_pointer = true;
-                } else {
-                    auto* disable_validation =
-                        ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace);
-                    auto* initializer = ctx.Clone(var->Declaration()->initializer);
-                    auto* local_var = ctx.dst->Var(new_var_symbol, store_type(), sc, initializer,
-                                                   tint::Vector{disable_validation});
-                    ctx.InsertFront(func->body->statements, ctx.dst->Decl(local_var));
-                }
-                break;
-            }
-            case core::AddressSpace::kPixelLocal:
-                break;  // Ignore
-            default: {
-                TINT_ICE() << "unhandled module-scope address space (" << sc << ")";
-            }
-        }
-    }
-
-    /// Process a variable `var` that is referenced in the user-defined function `func`.
-    /// This will redeclare the variable as a function parameter, possibly as a pointer.
-    /// @param func the user-defined function
-    /// @param var the variable
-    /// @param new_var_symbol the symbol to use for the replacement
-    /// @param is_pointer output signalling whether the replacement is a pointer or not
-    void ProcessVariableInUserFunction(const ast::Function* func,
-                                       const sem::Variable* var,
-                                       Symbol new_var_symbol,
-                                       bool& is_pointer) {
-        auto* ty = var->Type()->UnwrapRef();
-        auto param_type = CreateASTTypeFor(ctx, ty);
-        auto sc = var->AddressSpace();
-        switch (sc) {
-            case core::AddressSpace::kPrivate:
-                // Private variables are passed all together in a struct.
-                return;
-            case core::AddressSpace::kStorage:
-            case core::AddressSpace::kUniform:
-            case core::AddressSpace::kHandle:
-            case core::AddressSpace::kWorkgroup:
-                break;
-            case core::AddressSpace::kPushConstant: {
-                ctx.dst->Diagnostics().AddError(Source{})
-                    << "unhandled module-scope address space (" << sc << ")";
-                break;
-            }
-            default: {
-                TINT_ICE() << "unhandled module-scope address space (" << sc << ")";
-            }
-        }
-
-        // Use a pointer for non-handle types.
-        tint::Vector<const ast::Attribute*, 2> attributes;
-        if (!ty->IsHandle()) {
-            param_type = sc == core::AddressSpace::kStorage
-                             ? ctx.dst->ty.ptr(sc, param_type, var->Access())
-                             : ctx.dst->ty.ptr(sc, param_type);
-            is_pointer = true;
-
-            // Disable validation of the parameter's address space and of arguments passed to it.
-            attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
-            attributes.Push(
-                ctx.dst->Disable(ast::DisabledValidation::kIgnoreInvalidPointerArgument));
-        }
-
-        // Redeclare the variable as a parameter.
-        ctx.InsertBack(func->params,
-                       ctx.dst->Param(new_var_symbol, param_type, std::move(attributes)));
-    }
-
-    /// Replace all uses of `var` in `func` with references to `new_var`.
-    /// @param func the function
-    /// @param var the variable to replace
-    /// @param new_var the symbol to use for replacement
-    /// @param is_pointer true if `new_var` is a pointer to the new variable
-    /// @param member_name if valid, the name of the struct member that holds this variable
-    void ReplaceUsesInFunction(const ast::Function* func,
-                               const sem::Variable* var,
-                               Symbol new_var,
-                               bool is_pointer,
-                               Symbol member_name) {
-        for (auto* user : var->Users()) {
-            if (user->Stmt()->Function()->Declaration() == func) {
-                const ast::Expression* expr = ctx.dst->Expr(new_var);
-                if (is_pointer) {
-                    // If this identifier is used by an address-of operator, just remove the
-                    // address-of instead of adding a deref, since we already have a pointer.
-                    auto* ident = user->Declaration()->As<ast::IdentifierExpression>();
-                    if (ident_to_address_of_.count(ident) && !member_name.IsValid()) {
-                        ctx.Replace(ident_to_address_of_[ident], expr);
-                        continue;
-                    }
-
-                    expr = ctx.dst->Deref(expr);
-                }
-                if (member_name.IsValid()) {
-                    // Get the member from the containing structure.
-                    expr = ctx.dst->MemberAccessor(expr, member_name);
-                }
-                ctx.Replace(user->Declaration(), expr);
-            }
-        }
-    }
-
-    /// Process the module.
-    void Process() {
-        // Predetermine the list of function calls that need to be replaced.
-        using CallList = tint::Vector<const ast::CallExpression*, 8>;
-        std::unordered_map<const ast::Function*, CallList> calls_to_replace;
-
-        tint::Vector<const ast::Function*, 8> functions_to_process;
-
-        // Collect private variables into a single structure.
-        StructMemberList private_struct_members;
-        tint::Vector<std::function<const ast::AssignmentStatement*()>, 4> private_initializers;
-        std::unordered_set<const ast::Function*> uses_privates;
-
-        // Build a list of functions that transitively reference any module-scope variables.
-        for (auto* decl : ctx.src->Sem().Module()->DependencyOrderedDeclarations()) {
-            if (auto* var = decl->As<ast::Var>()) {
-                auto* sem_var = ctx.src->Sem().Get(var);
-                if (sem_var->AddressSpace() == core::AddressSpace::kPrivate) {
-                    // Create a member in the private variable struct.
-                    auto* ty = sem_var->Type()->UnwrapRef();
-                    auto name = ctx.Clone(var->name->symbol);
-                    private_struct_members.Push(ctx.dst->Member(name, CreateASTTypeFor(ctx, ty)));
-                    CloneStructTypes(ty);
-
-                    // Create a statement to assign the initializer if present.
-                    if (var->initializer) {
-                        private_initializers.Push([&, name, var] {
-                            return ctx.dst->Assign(
-                                ctx.dst->MemberAccessor(PrivateStructVariableName(), name),
-                                ctx.Clone(var->initializer));
-                        });
-                    }
-                }
-                continue;
-            }
-
-            auto* func_ast = decl->As<ast::Function>();
-            if (!func_ast) {
-                continue;
-            }
-
-            auto* func_sem = ctx.src->Sem().Get(func_ast);
-
-            bool needs_processing = false;
-            for (auto* var : func_sem->TransitivelyReferencedGlobals()) {
-                if (var->AddressSpace() != core::AddressSpace::kUndefined) {
-                    if (var->AddressSpace() == core::AddressSpace::kPrivate) {
-                        uses_privates.insert(func_ast);
-                    }
-                    needs_processing = true;
-                }
-            }
-            if (needs_processing) {
-                functions_to_process.Push(func_ast);
-
-                // Find all of the calls to this function that will need to be replaced.
-                for (auto* call : func_sem->CallSites()) {
-                    calls_to_replace[call->Stmt()->Function()->Declaration()].Push(
-                        call->Declaration());
-                }
-            }
-        }
-
-        if (!private_struct_members.IsEmpty()) {
-            // Create the private variable struct.
-            ctx.dst->Structure(PrivateStructName(), std::move(private_struct_members));
-        }
-
-        // Build a list of `&ident` expressions. We'll use this later to avoid generating
-        // expressions of the form `&*ident`, which break WGSL validation rules when this expression
-        // is passed to a function.
-        // TODO(jrprice): We should add support for bidirectional SEM tree traversal so that we can
-        // do this on the fly instead.
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
-            auto* address_of = node->As<ast::UnaryOpExpression>();
-            if (!address_of || address_of->op != core::UnaryOp::kAddressOf) {
-                continue;
-            }
-            if (auto* ident = address_of->expr->As<ast::IdentifierExpression>()) {
-                ident_to_address_of_[ident] = address_of;
-            }
-        }
-
-        for (auto* func_ast : functions_to_process) {
-            auto* func_sem = ctx.src->Sem().Get(func_ast);
-            bool is_entry_point = func_ast->IsEntryPoint();
-            bool needs_pointer_aliasing = false;
-
-            // Map module-scope variables onto their replacement.
-            struct NewVar {
-                Symbol symbol;
-                bool is_pointer;
-                bool is_wrapped;
-            };
-            std::unordered_map<const sem::Variable*, NewVar> var_to_newvar;
-
-            // We aggregate all workgroup variables into a struct to avoid hitting MSL's limit for
-            // threadgroup memory arguments.
-            Symbol workgroup_parameter_symbol;
-            StructMemberList workgroup_parameter_members;
-            auto workgroup_param = [&] {
-                if (!workgroup_parameter_symbol.IsValid()) {
-                    workgroup_parameter_symbol = ctx.dst->Sym();
-                }
-                return workgroup_parameter_symbol;
-            };
-
-            // If this function references any private variables, it needs to take the private
-            // variable struct as a parameter (or declare it, if it is an entry point function).
-            if (uses_privates.count(func_ast)) {
-                if (is_entry_point) {
-                    // Create a local declaration for the private variable struct.
-                    auto* var = ctx.dst->Var(
-                        PrivateStructVariableName(), ctx.dst->ty(PrivateStructName()),
-                        core::AddressSpace::kPrivate,
-                        tint::Vector{
-                            ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace),
-                        });
-                    ctx.InsertFront(func_ast->body->statements, ctx.dst->Decl(var));
-
-                    // Initialize the members of that struct with the original initializers.
-                    for (auto init : private_initializers) {
-                        ctx.InsertFront(func_ast->body->statements, init());
-                    }
-                } else {
-                    // Create a parameter that is a pointer to the private variable struct.
-                    auto ptr = ctx.dst->ty.ptr<private_>(ctx.dst->ty(PrivateStructName()));
-                    auto* param = ctx.dst->Param(PrivateStructVariableName(), ptr);
-                    ctx.InsertBack(func_ast->params, param);
-                }
-            }
-
-            // Process and redeclare all variables referenced by the function.
-            for (auto* var : func_sem->TransitivelyReferencedGlobals()) {
-                if (var->AddressSpace() == core::AddressSpace::kUndefined) {
-                    continue;
-                }
-                if (var->AddressSpace() == core::AddressSpace::kPrivate) {
-                    // Private variable are collected into a single struct that is passed by
-                    // pointer (handled above), so we just need to replace the uses here.
-                    ReplaceUsesInFunction(func_ast, var, PrivateStructVariableName(),
-                                          /* is_pointer */ !is_entry_point,
-                                          ctx.Clone(var->Declaration()->name->symbol));
-                    continue;
-                }
-
-                // This is the symbol for the variable that replaces the module-scope var.
-                auto new_var_symbol = ctx.dst->Sym();
-
-                // Track whether the new variable is a pointer or not.
-                bool is_pointer = false;
-
-                // Track whether the new variable was wrapped in a struct or not.
-                bool is_wrapped = false;
-
-                // Process the variable to redeclare it as a parameter or local variable.
-                if (is_entry_point) {
-                    ProcessVariableInEntryPoint(func_ast, var, new_var_symbol, workgroup_param,
-                                                workgroup_parameter_members, is_pointer,
-                                                is_wrapped);
-                } else {
-                    ProcessVariableInUserFunction(func_ast, var, new_var_symbol, is_pointer);
-                    if (var->AddressSpace() == core::AddressSpace::kWorkgroup) {
-                        needs_pointer_aliasing = true;
-                    }
-                }
-
-                // Record the replacement symbol.
-                var_to_newvar[var] = {new_var_symbol, is_pointer, is_wrapped};
-
-                // Replace all uses of the module-scope variable.
-                ReplaceUsesInFunction(
-                    func_ast, var, new_var_symbol, is_pointer,
-                    is_wrapped ? ctx.dst->Sym(kWrappedArrayMemberName) : Symbol());
-            }
-
-            // Allow pointer aliasing if needed.
-            if (needs_pointer_aliasing) {
-                ctx.InsertBack(func_ast->attributes,
-                               ctx.dst->Disable(ast::DisabledValidation::kIgnorePointerAliasing));
-            }
-
-            if (!workgroup_parameter_members.IsEmpty()) {
-                // Create the workgroup memory parameter.
-                // The parameter is a struct that contains members for each workgroup variable.
-                auto* str =
-                    ctx.dst->Structure(ctx.dst->Sym(), std::move(workgroup_parameter_members));
-                auto param_type = ctx.dst->ty.ptr(workgroup, ctx.dst->ty.Of(str));
-                auto* param = ctx.dst->Param(
-                    workgroup_param(), param_type,
-                    tint::Vector{
-                        ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter),
-                        ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace),
-                    });
-                ctx.InsertFront(func_ast->params, param);
-            }
-
-            // Pass the variables as pointers to any functions that need them.
-            for (auto* call : calls_to_replace[func_ast]) {
-                auto* call_sem = ctx.src->Sem().Get(call)->Unwrap()->As<sem::Call>();
-                auto* target_sem = call_sem->Target()->As<sem::Function>();
-
-                // Pass the private variable struct pointer if needed.
-                if (uses_privates.count(target_sem->Declaration())) {
-                    const ast::Expression* arg = ctx.dst->Expr(PrivateStructVariableName());
-                    if (is_entry_point) {
-                        arg = ctx.dst->AddressOf(arg);
-                    }
-                    ctx.InsertBack(call->args, arg);
-                }
-
-                // Add new arguments for any variables that are needed by the callee.
-                // For entry points, pass non-handle types as pointers.
-                for (auto* target_var : target_sem->TransitivelyReferencedGlobals()) {
-                    auto sc = target_var->AddressSpace();
-                    if (sc == core::AddressSpace::kUndefined) {
-                        continue;
-                    }
-
-                    auto it = var_to_newvar.find(target_var);
-                    if (it == var_to_newvar.end()) {
-                        // No replacement was created for this function.
-                        continue;
-                    }
-
-                    auto new_var = it->second;
-                    bool IsHandle = target_var->Type()->UnwrapRef()->IsHandle();
-                    const ast::Expression* arg = ctx.dst->Expr(new_var.symbol);
-                    if (new_var.is_wrapped) {
-                        // The variable is wrapped in a struct, so we need to pass a pointer to the
-                        // struct member instead.
-                        arg = ctx.dst->AddressOf(
-                            ctx.dst->MemberAccessor(ctx.dst->Deref(arg), kWrappedArrayMemberName));
-                    } else if (is_entry_point && !IsHandle && !new_var.is_pointer) {
-                        // We need to pass a pointer and we don't already have one, so take
-                        // the address of the new variable.
-                        arg = ctx.dst->AddressOf(arg);
-                    }
-                    ctx.InsertBack(call->args, arg);
-                }
-            }
-        }
-
-        // Now remove all module-scope variables with these address spaces.
-        for (auto* var_ast : ctx.src->AST().GlobalVariables()) {
-            auto* var_sem = ctx.src->Sem().Get(var_ast);
-            if (var_sem->AddressSpace() != core::AddressSpace::kUndefined) {
-                ctx.Remove(ctx.src->AST().GlobalDeclarations(), var_ast);
-            }
-        }
-    }
-
-    /// @returns the name of the structure that contains all of the module-scope private variables
-    Symbol PrivateStructName() {
-        if (!private_struct_name.IsValid()) {
-            private_struct_name = ctx.dst->Symbols().New("tint_private_vars_struct");
-        }
-        return private_struct_name;
-    }
-
-    /// @returns the name of the variable that contains all of the module-scope private variables
-    Symbol PrivateStructVariableName() {
-        if (!private_struct_variable_name.IsValid()) {
-            private_struct_variable_name = ctx.dst->Symbols().New("tint_private_vars");
-        }
-        return private_struct_variable_name;
-    }
-
-  private:
-    // The structures that have already been cloned by this transform.
-    std::unordered_set<const sem::Struct*> cloned_structs_;
-
-    // Map from identifier expression to the address-of expression that uses it.
-    std::unordered_map<const ast::IdentifierExpression*, const ast::UnaryOpExpression*>
-        ident_to_address_of_;
-
-    // The name of the structure that contains all the module-scope private variables.
-    Symbol private_struct_name;
-
-    // The name of the structure variable that contains all the module-scope private variables.
-    Symbol private_struct_variable_name;
-};
-
-ModuleScopeVarToEntryPointParam::ModuleScopeVarToEntryPointParam() = default;
-
-ModuleScopeVarToEntryPointParam::~ModuleScopeVarToEntryPointParam() = default;
-
-ast::transform::Transform::ApplyResult ModuleScopeVarToEntryPointParam::Apply(
-    const Program& src,
-    const ast::transform::DataMap&,
-    ast::transform::DataMap&) const {
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-    State state{ctx};
-    state.Process();
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.h b/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.h
deleted file mode 100644
index 44cfc33..0000000
--- a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_MSL_WRITER_AST_RAISE_MODULE_SCOPE_VAR_TO_ENTRY_POINT_PARAM_H_
-#define SRC_TINT_LANG_MSL_WRITER_AST_RAISE_MODULE_SCOPE_VAR_TO_ENTRY_POINT_PARAM_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::msl::writer {
-
-/// Move module-scope variables into the entry point as parameters.
-///
-/// MSL does not allow module-scope variables to have any address space other
-/// than `constant`. This transform moves all module-scope declarations into the
-/// entry point function (either as parameters or function-scope variables) and
-/// then passes them as pointer parameters to any function that references them.
-///
-/// Since WGSL does not allow entry point parameters or function-scope variables
-/// to have these address spaces, we annotate the new variable declarations
-/// with an attribute that bypasses that validation rule.
-///
-/// Before:
-/// ```
-/// struct S {
-///   f : f32;
-/// };
-/// @binding(0) @group(0)
-/// var<storage, read> s : S;
-/// var<private> p : f32 = 2.0;
-///
-/// fn foo() {
-///   p = p + f;
-/// }
-///
-/// @compute @workgroup_size(1)
-/// fn main() {
-///   foo();
-/// }
-/// ```
-///
-/// After:
-/// ```
-/// fn foo(p : ptr<private, f32>, sptr : ptr<storage, S, read>) {
-///   *p = *p + (*sptr).f;
-/// }
-///
-/// @compute @workgroup_size(1)
-/// fn main(sptr : ptr<storage, S, read>) {
-///   var<private> p : f32 = 2.0;
-///   foo(&p, sptr);
-/// }
-/// ```
-class ModuleScopeVarToEntryPointParam final
-    : public Castable<ModuleScopeVarToEntryPointParam, ast::transform::Transform> {
-  public:
-    /// Constructor
-    ModuleScopeVarToEntryPointParam();
-    /// Destructor
-    ~ModuleScopeVarToEntryPointParam() override;
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::msl::writer
-
-#endif  // SRC_TINT_LANG_MSL_WRITER_AST_RAISE_MODULE_SCOPE_VAR_TO_ENTRY_POINT_PARAM_H_
diff --git a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param_test.cc b/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param_test.cc
deleted file mode 100644
index 8a24325..0000000
--- a/src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param_test.cc
+++ /dev/null
@@ -1,1363 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/module_scope_var_to_entry_point_param.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using ModuleScopeVarToEntryPointParamTest = ast::transform::TransformTest;
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<ModuleScopeVarToEntryPointParam>(src));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, ShouldRunHasGlobal) {
-    auto* src = R"(
-var<private> v : i32;
-)";
-
-    EXPECT_TRUE(ShouldRun<ModuleScopeVarToEntryPointParam>(src));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Basic) {
-    auto* src = R"(
-var<private> p : f32;
-var<workgroup> w : f32;
-
-@compute @workgroup_size(1)
-fn main() {
-  w = p;
-}
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  p : f32,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol : f32;
-  tint_symbol = tint_private_vars.p;
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Basic_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  w = p;
-}
-
-var<workgroup> w : f32;
-var<private> p : f32;
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  p : f32,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol : f32;
-  tint_symbol = tint_private_vars.p;
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, FunctionCalls) {
-    auto* src = R"(
-var<private> p : f32;
-var<workgroup> w : f32;
-
-fn no_uses() {
-}
-
-fn zoo() {
-  p = p * 2.0;
-}
-
-fn bar(a : f32, b : f32) {
-  p = a;
-  w = b;
-  zoo();
-}
-
-fn foo(a : f32) {
-  let b : f32 = 2.0;
-  bar(a, b);
-  no_uses();
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  foo(1.0);
-}
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  p : f32,
-}
-
-fn no_uses() {
-}
-
-fn zoo(tint_private_vars : ptr<private, tint_private_vars_struct>) {
-  (*(tint_private_vars)).p = ((*(tint_private_vars)).p * 2.0);
-}
-
-@internal(disable_validation__ignore_pointer_aliasing)
-fn bar(a : f32, b : f32, tint_private_vars : ptr<private, tint_private_vars_struct>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
-  (*(tint_private_vars)).p = a;
-  *(tint_symbol) = b;
-  zoo(tint_private_vars);
-}
-
-@internal(disable_validation__ignore_pointer_aliasing)
-fn foo(a : f32, tint_private_vars : ptr<private, tint_private_vars_struct>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<workgroup, f32>) {
-  let b : f32 = 2.0;
-  bar(a, b, tint_private_vars, tint_symbol_1);
-  no_uses();
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_2 : f32;
-  foo(1.0, &(tint_private_vars), &(tint_symbol_2));
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, FunctionCalls_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  foo(1.0);
-}
-
-fn foo(a : f32) {
-  let b : f32 = 2.0;
-  bar(a, b);
-  no_uses();
-}
-
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32) {
-  p = a;
-  w = b;
-  zoo();
-}
-
-fn zoo() {
-  p = p * 2.0;
-}
-
-var<private> p : f32;
-var<workgroup> w : f32;
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  p : f32,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_2 : f32;
-  foo(1.0, &(tint_private_vars), &(tint_symbol_2));
-}
-
-@internal(disable_validation__ignore_pointer_aliasing)
-fn foo(a : f32, tint_private_vars : ptr<private, tint_private_vars_struct>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<workgroup, f32>) {
-  let b : f32 = 2.0;
-  bar(a, b, tint_private_vars, tint_symbol_1);
-  no_uses();
-}
-
-fn no_uses() {
-}
-
-@internal(disable_validation__ignore_pointer_aliasing)
-fn bar(a : f32, b : f32, tint_private_vars : ptr<private, tint_private_vars_struct>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
-  (*(tint_private_vars)).p = a;
-  *(tint_symbol) = b;
-  zoo(tint_private_vars);
-}
-
-fn zoo(tint_private_vars : ptr<private, tint_private_vars_struct>) {
-  (*(tint_private_vars)).p = ((*(tint_private_vars)).p * 2.0);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Initializers) {
-    auto* src = R"(
-var<private> a : f32 = 1.0;
-var<private> b : f32 = f32();
-
-@compute @workgroup_size(1)
-fn main() {
-  let x : f32 = a + b;
-}
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  a : f32,
-  b : f32,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  tint_private_vars.a = 1.0;
-  tint_private_vars.b = f32();
-  let x : f32 = (tint_private_vars.a + tint_private_vars.b);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Initializers_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  let x : f32 = a + b;
-}
-
-var<private> b : f32 = f32();
-var<private> a : f32 = 1.0;
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  a : f32,
-  b : f32,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  tint_private_vars.a = 1.0;
-  tint_private_vars.b = f32();
-  let x : f32 = (tint_private_vars.a + tint_private_vars.b);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Pointers) {
-    auto* src = R"(
-var<private> p : f32;
-var<workgroup> w : f32;
-
-@compute @workgroup_size(1)
-fn main() {
-  let p_ptr : ptr<private, f32> = &p;
-  let w_ptr : ptr<workgroup, f32> = &w;
-  let x : f32 = *p_ptr + *w_ptr;
-  *p_ptr = x;
-}
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  p : f32,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol : f32;
-  let p_ptr : ptr<private, f32> = &(tint_private_vars.p);
-  let w_ptr : ptr<workgroup, f32> = &(tint_symbol);
-  let x : f32 = (*(p_ptr) + *(w_ptr));
-  *(p_ptr) = x;
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Pointers_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  let p_ptr : ptr<private, f32> = &p;
-  let w_ptr : ptr<workgroup, f32> = &w;
-  let x : f32 = *p_ptr + *w_ptr;
-  *p_ptr = x;
-}
-
-var<workgroup> w : f32;
-var<private> p : f32;
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  p : f32,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol : f32;
-  let p_ptr : ptr<private, f32> = &(tint_private_vars.p);
-  let w_ptr : ptr<workgroup, f32> = &(tint_symbol);
-  let x : f32 = (*(p_ptr) + *(w_ptr));
-  *(p_ptr) = x;
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// TODO(crbug.com/tint/1758): Requires support for workgroup pointer parameters, which is
-// unsupported until WGSL 1.1
-TEST_F(ModuleScopeVarToEntryPointParamTest, DISABLED_FoldAddressOfDeref) {
-    auto* src = R"(
-var<workgroup> v : f32;
-
-fn bar(p : ptr<workgroup, f32>) {
-  (*p) = 0.0;
-}
-
-fn foo() {
-  bar(&v);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  foo();
-}
-)";
-
-    auto* expect = R"(
-fn bar(p : ptr<workgroup, f32>) {
-  *(p) = 0.0;
-}
-
-fn foo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
-  bar(tint_symbol);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_1 : f32;
-  foo(&(tint_symbol_1));
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// TODO(crbug.com/tint/1758): Requires support for workgroup pointer parameters, which is
-// unsupported until WGSL 1.1
-TEST_F(ModuleScopeVarToEntryPointParamTest, DISABLED_FoldAddressOfDeref_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  foo();
-}
-
-fn foo() {
-  bar(&v);
-}
-
-fn bar(p : ptr<workgroup, f32>) {
-  (*p) = 0.0;
-}
-
-var<workgroup> v : f32;
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_1 : f32;
-  foo(&(tint_symbol_1));
-}
-
-fn foo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
-  bar(tint_symbol);
-}
-
-fn bar(p : ptr<workgroup, f32>) {
-  *(p) = 0.0;
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_Basic) {
-    auto* src = R"(
-struct S {
-  a : f32,
-};
-
-@group(0) @binding(0)
-var<uniform> u : S;
-@group(0) @binding(1)
-var<storage> s : S;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = u;
-  _ = s;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : f32,
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<storage, S, read>) {
-  _ = *(tint_symbol);
-  _ = *(tint_symbol_1);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_Basic_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  _ = u;
-  _ = s;
-}
-
-@group(0) @binding(0) var<uniform> u : S;
-@group(0) @binding(1) var<storage> s : S;
-
-struct S {
-  a : f32,
-};
-
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<storage, S, read>) {
-  _ = *(tint_symbol);
-  _ = *(tint_symbol_1);
-}
-
-struct S {
-  a : f32,
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray) {
-    auto* src = R"(
-@group(0) @binding(0)
-var<storage> buffer : array<f32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = buffer[0];
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  arr : array<f32>,
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1, read>) {
-  _ = (*(tint_symbol)).arr[0];
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  _ = buffer[0];
-}
-
-@group(0) @binding(0)
-var<storage> buffer : array<f32>;
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  arr : array<f32>,
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1, read>) {
-  _ = (*(tint_symbol)).arr[0];
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArrayInsideFunction) {
-    auto* src = R"(
-@group(0) @binding(0)
-var<storage> buffer : array<f32>;
-
-fn foo() {
-  _ = buffer[0];
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  foo();
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  arr : array<f32>,
-}
-
-fn foo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<storage, array<f32>, read>) {
-  _ = (*(tint_symbol))[0];
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<storage, tint_symbol_2, read>) {
-  foo(&((*(tint_symbol_1)).arr));
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArrayInsideFunction_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  foo();
-}
-
-fn foo() {
-  _ = buffer[0];
-}
-
-@group(0) @binding(0) var<storage> buffer : array<f32>;
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  arr : array<f32>,
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<storage, tint_symbol_2, read>) {
-  foo(&((*(tint_symbol_1)).arr));
-}
-
-fn foo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<storage, array<f32>, read>) {
-  _ = (*(tint_symbol))[0];
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray_Alias) {
-    auto* src = R"(
-alias myarray = array<f32>;
-
-@group(0) @binding(0)
-var<storage> buffer : myarray;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = buffer[0];
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  arr : array<f32>,
-}
-
-alias myarray = array<f32>;
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1, read>) {
-  _ = (*(tint_symbol)).arr[0];
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_RuntimeArray_Alias_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  _ = buffer[0];
-}
-
-@group(0) @binding(0) var<storage> buffer : myarray;
-
-alias myarray = array<f32>;
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  arr : array<f32>,
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1, read>) {
-  _ = (*(tint_symbol)).arr[0];
-}
-
-alias myarray = array<f32>;
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_ArrayOfStruct) {
-    auto* src = R"(
-struct S {
-  f : f32,
-};
-
-@group(0) @binding(0)
-var<storage> buffer : array<S>;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = buffer[0];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  f : f32,
-}
-
-struct tint_symbol_1 {
-  arr : array<S>,
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1, read>) {
-  _ = (*(tint_symbol)).arr[0];
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffer_ArrayOfStruct_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  _ = buffer[0];
-}
-
-@group(0) @binding(0) var<storage> buffer : array<S>;
-
-struct S {
-  f : f32,
-};
-)";
-
-    auto* expect = R"(
-struct S {
-  f : f32,
-}
-
-struct tint_symbol_1 {
-  arr : array<S>,
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1, read>) {
-  _ = (*(tint_symbol)).arr[0];
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_FunctionCalls) {
-    auto* src = R"(
-struct S {
-  a : f32,
-};
-
-@group(0) @binding(0)
-var<uniform> u : S;
-@group(0) @binding(1)
-var<storage> s : S;
-
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32) {
-  _ = u;
-  _ = s;
-}
-
-fn foo(a : f32) {
-  let b : f32 = 2.0;
-  _ = u;
-  bar(a, b);
-  no_uses();
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  foo(1.0);
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : f32,
-}
-
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<uniform, S>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<storage, S, read>) {
-  _ = *(tint_symbol);
-  _ = *(tint_symbol_1);
-}
-
-fn foo(a : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<uniform, S>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<storage, S, read>) {
-  let b : f32 = 2.0;
-  _ = *(tint_symbol_2);
-  bar(a, b, tint_symbol_2, tint_symbol_3);
-  no_uses();
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_4 : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_5 : ptr<storage, S, read>) {
-  foo(1.0, tint_symbol_4, tint_symbol_5);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Buffers_FunctionCalls_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  foo(1.0);
-}
-
-fn foo(a : f32) {
-  let b : f32 = 2.0;
-  _ = u;
-  bar(a, b);
-  no_uses();
-}
-
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32) {
-  _ = u;
-  _ = s;
-}
-
-struct S {
-  a : f32,
-};
-
-@group(0) @binding(0)
-var<uniform> u : S;
-@group(0) @binding(1)
-var<storage> s : S;
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_4 : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_5 : ptr<storage, S, read>) {
-  foo(1.0, tint_symbol_4, tint_symbol_5);
-}
-
-fn foo(a : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<uniform, S>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<storage, S, read>) {
-  let b : f32 = 2.0;
-  _ = *(tint_symbol_2);
-  bar(a, b, tint_symbol_2, tint_symbol_3);
-  no_uses();
-}
-
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<uniform, S>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<storage, S, read>) {
-  _ = *(tint_symbol);
-  _ = *(tint_symbol_1);
-}
-
-struct S {
-  a : f32,
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, HandleTypes_Basic) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-@group(0) @binding(1) var s : sampler;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = t;
-  _ = s;
-}
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) tint_symbol : texture_2d<f32>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) tint_symbol_1 : sampler) {
-  _ = tint_symbol;
-  _ = tint_symbol_1;
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, HandleTypes_FunctionCalls) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-@group(0) @binding(1) var s : sampler;
-
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32) {
-  _ = t;
-  _ = s;
-}
-
-fn foo(a : f32) {
-  let b : f32 = 2.0;
-  _ = t;
-  bar(a, b);
-  no_uses();
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  foo(1.0);
-}
-)";
-
-    auto* expect = R"(
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32, tint_symbol : texture_2d<f32>, tint_symbol_1 : sampler) {
-  _ = tint_symbol;
-  _ = tint_symbol_1;
-}
-
-fn foo(a : f32, tint_symbol_2 : texture_2d<f32>, tint_symbol_3 : sampler) {
-  let b : f32 = 2.0;
-  _ = tint_symbol_2;
-  bar(a, b, tint_symbol_2, tint_symbol_3);
-  no_uses();
-}
-
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) tint_symbol_4 : texture_2d<f32>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) tint_symbol_5 : sampler) {
-  foo(1.0, tint_symbol_4, tint_symbol_5);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, HandleTypes_FunctionCalls_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  foo(1.0);
-}
-
-fn foo(a : f32) {
-  let b : f32 = 2.0;
-  _ = t;
-  bar(a, b);
-  no_uses();
-}
-
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32) {
-  _ = t;
-  _ = s;
-}
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-@group(0) @binding(1) var s : sampler;
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) tint_symbol_4 : texture_2d<f32>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) tint_symbol_5 : sampler) {
-  foo(1.0, tint_symbol_4, tint_symbol_5);
-}
-
-fn foo(a : f32, tint_symbol_2 : texture_2d<f32>, tint_symbol_3 : sampler) {
-  let b : f32 = 2.0;
-  _ = tint_symbol_2;
-  bar(a, b, tint_symbol_2, tint_symbol_3);
-  no_uses();
-}
-
-fn no_uses() {
-}
-
-fn bar(a : f32, b : f32, tint_symbol : texture_2d<f32>, tint_symbol_1 : sampler) {
-  _ = tint_symbol;
-  _ = tint_symbol_1;
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, Matrix) {
-    auto* src = R"(
-var<workgroup> m : mat2x2<f32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  let x = m;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  m : mat2x2<f32>,
-}
-
-@compute @workgroup_size(1)
-fn main(@internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<workgroup, tint_symbol_2>) {
-  let tint_symbol = &((*(tint_symbol_1)).m);
-  let x = *(tint_symbol);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, NestedMatrix) {
-    auto* src = R"(
-struct S1 {
-  m : mat2x2<f32>,
-};
-struct S2 {
-  s : S1,
-};
-var<workgroup> m : array<S2, 4>;
-
-@compute @workgroup_size(1)
-fn main() {
-  let x = m;
-}
-)";
-
-    auto* expect = R"(
-struct S1 {
-  m : mat2x2<f32>,
-}
-
-struct S2 {
-  s : S1,
-}
-
-struct tint_symbol_2 {
-  m : array<S2, 4u>,
-}
-
-@compute @workgroup_size(1)
-fn main(@internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<workgroup, tint_symbol_2>) {
-  let tint_symbol = &((*(tint_symbol_1)).m);
-  let x = *(tint_symbol);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that we do not duplicate a struct type used by multiple workgroup
-// variables that are promoted to threadgroup memory arguments.
-TEST_F(ModuleScopeVarToEntryPointParamTest, DuplicateThreadgroupArgumentTypes) {
-    auto* src = R"(
-struct S {
-  m : mat2x2<f32>,
-};
-
-var<workgroup> a : S;
-
-var<workgroup> b : S;
-
-@compute @workgroup_size(1)
-fn main() {
-  let x = a;
-  let y = b;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat2x2<f32>,
-}
-
-struct tint_symbol_3 {
-  a : S,
-  b : S,
-}
-
-@compute @workgroup_size(1)
-fn main(@internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<workgroup, tint_symbol_3>) {
-  let tint_symbol = &((*(tint_symbol_1)).a);
-  let tint_symbol_2 = &((*(tint_symbol_1)).b);
-  let x = *(tint_symbol);
-  let y = *(tint_symbol_2);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that we do not duplicate a struct type used by multiple workgroup
-// variables that are promoted to threadgroup memory arguments.
-TEST_F(ModuleScopeVarToEntryPointParamTest, DuplicateThreadgroupArgumentTypes_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  let x = a;
-  let y = b;
-}
-
-var<workgroup> a : S;
-var<workgroup> b : S;
-
-struct S {
-  m : mat2x2<f32>,
-};
-)";
-
-    auto* expect = R"(
-struct S {
-  m : mat2x2<f32>,
-}
-
-struct tint_symbol_3 {
-  a : S,
-  b : S,
-}
-
-@compute @workgroup_size(1)
-fn main(@internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<workgroup, tint_symbol_3>) {
-  let tint_symbol = &((*(tint_symbol_1)).a);
-  let tint_symbol_2 = &((*(tint_symbol_1)).b);
-  let x = *(tint_symbol);
-  let y = *(tint_symbol_2);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, UnusedVariables) {
-    auto* src = R"(
-struct S {
-  a : f32,
-};
-
-var<private> p : f32;
-var<workgroup> w : f32;
-var<private> p_with_init : f32 = 42;
-
-@group(0) @binding(0)
-var<uniform> ub : S;
-@group(0) @binding(1)
-var<storage> sb : S;
-
-@group(0) @binding(2) var t : texture_2d<f32>;
-@group(0) @binding(3) var s : sampler;
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
-    auto* expect = R"(
-struct tint_private_vars_struct {
-  p : f32,
-  p_with_init : f32,
-}
-
-struct S {
-  a : f32,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, MultiplePrivateVariables) {
-    auto* src = R"(
-struct S {
-  a : f32,
-  b : f32,
-  c : f32,
-}
-
-var<private> a : f32;
-var<private> b : f32 = 42;
-var<private> c : S = S(1, 2, 3);
-var<private> d : S;
-var<private> unused : f32;
-
-fn foo(x : f32) -> f32 {
-  return (a + b + c.a + d.c) * x;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = foo(1.0);
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : f32,
-  b : f32,
-  c : f32,
-}
-
-struct tint_private_vars_struct {
-  a : f32,
-  b : f32,
-  c : S,
-  d : S,
-  unused : f32,
-}
-
-fn foo(x : f32, tint_private_vars : ptr<private, tint_private_vars_struct>) -> f32 {
-  return (((((*(tint_private_vars)).a + (*(tint_private_vars)).b) + (*(tint_private_vars)).c.a) + (*(tint_private_vars)).d.c) * x);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  tint_private_vars.b = 42;
-  tint_private_vars.c = S(1, 2, 3);
-  _ = foo(1.0, &(tint_private_vars));
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, MultiplePrivateVariables_OutOfOrder) {
-    auto* src = R"(
-var<private> a : f32;
-var<private> c : S = S(1, 2, 3);
-var<private> unused : f32;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = foo(1.0);
-}
-
-fn foo(x : f32) -> f32 {
-  return (a + b + c.a + d.c) * x;
-}
-
-var<private> b : f32 = 42;
-
-struct S {
-  a : f32,
-  b : f32,
-  c : f32,
-}
-
-var<private> d : S;
-)";
-
-    auto* expect = R"(
-struct S {
-  a : f32,
-  b : f32,
-  c : f32,
-}
-
-struct tint_private_vars_struct {
-  a : f32,
-  c : S,
-  unused : f32,
-  b : f32,
-  d : S,
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  @internal(disable_validation__ignore_address_space) var<private> tint_private_vars : tint_private_vars_struct;
-  tint_private_vars.c = S(1, 2, 3);
-  tint_private_vars.b = 42;
-  _ = foo(1.0, &(tint_private_vars));
-}
-
-fn foo(x : f32, tint_private_vars : ptr<private, tint_private_vars_struct>) -> f32 {
-  return (((((*(tint_private_vars)).a + (*(tint_private_vars)).b) + (*(tint_private_vars)).c.a) + (*(tint_private_vars)).d.c) * x);
-}
-)";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ModuleScopeVarToEntryPointParamTest, EmtpyModule) {
-    auto* src = "";
-
-    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
-
-    EXPECT_EQ(src, str(got));
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/packed_vec3.cc b/src/tint/lang/msl/writer/ast_raise/packed_vec3.cc
deleted file mode 100644
index 4081fac..0000000
--- a/src/tint/lang/msl/writer/ast_raise/packed_vec3.cc
+++ /dev/null
@@ -1,566 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/packed_vec3.h"
-
-#include <algorithm>
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/core/builtin_type.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/array.h"
-#include "src/tint/lang/core/type/reference.h"
-#include "src/tint/lang/core/type/vector.h"
-#include "src/tint/lang/wgsl/ast/assignment_statement.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/array_count.h"
-#include "src/tint/lang/wgsl/sem/index_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/load.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/type_expression.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/hashmap.h"
-#include "src/tint/utils/containers/hashset.h"
-#include "src/tint/utils/containers/vector.h"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::PackedVec3);
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-
-/// Arrays larger than this will be packed/unpacked with a for loop.
-/// Arrays up to this size will be packed/unpacked with a sequence of statements.
-static constexpr uint32_t kMaxSeriallyUnpackedArraySize = 8;
-
-/// PIMPL state for the transform
-struct PackedVec3::State {
-    /// Constructor
-    /// @param program the source program
-    explicit State(const Program& program) : src(program) {}
-
-    /// The name of the struct member used when wrapping packed vec3 types.
-    static constexpr const char* kStructMemberName = "elements";
-
-    /// The names of the structures used to wrap packed vec3 types.
-    Hashmap<const core::type::Type*, Symbol, 4> packed_vec3_wrapper_struct_names;
-
-    /// A cache of host-shareable structures that have been rewritten.
-    Hashmap<const core::type::Type*, Symbol, 4> rewritten_structs;
-
-    /// A map from type to the name of a helper function used to pack that type.
-    Hashmap<const core::type::Type*, Symbol, 4> pack_helpers;
-
-    /// A map from type to the name of a helper function used to unpack that type.
-    Hashmap<const core::type::Type*, Symbol, 4> unpack_helpers;
-
-    /// @returns true if @p addrspace requires vec3 types to be packed
-    bool AddressSpaceNeedsPacking(core::AddressSpace addrspace) {
-        // Host-shareable address spaces need to be packed to match the memory layout on the host.
-        // The workgroup address space needs to be packed so that the size of generated threadgroup
-        // variables matches the size of the original WGSL declarations.
-        return core::IsHostShareable(addrspace) || addrspace == core::AddressSpace::kWorkgroup;
-    }
-
-    /// @param ty the type to test
-    /// @returns true if `ty` is a vec3, false otherwise
-    bool IsVec3(const core::type::Type* ty) {
-        if (auto* vec = ty->As<core::type::Vector>()) {
-            if (vec->Width() == 3) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /// @param ty the type to test
-    /// @returns true if `ty` is or contains a vec3, false otherwise
-    bool ContainsVec3(const core::type::Type* ty) {
-        return Switch(
-            ty,  //
-            [&](const core::type::Vector* vec) { return IsVec3(vec); },
-            [&](const core::type::Matrix* mat) { return ContainsVec3(mat->ColumnType()); },
-            [&](const core::type::Array* arr) { return ContainsVec3(arr->ElemType()); },
-            [&](const core::type::Struct* str) {
-                for (auto* member : str->Members()) {
-                    if (ContainsVec3(member->Type())) {
-                        return true;
-                    }
-                }
-                return false;
-            });
-    }
-
-    /// Create a `__packed_vec3` type with the same element type as `ty`.
-    /// @param ty a three-element vector type
-    /// @returns the new AST type
-    ast::Type MakePackedVec3(const core::type::Type* ty) {
-        auto* vec = ty->As<core::type::Vector>();
-        TINT_ASSERT(vec != nullptr && vec->Width() == 3);
-        return b.ty(core::BuiltinType::kPackedVec3, CreateASTTypeFor(ctx, vec->Type()));
-    }
-
-    /// Recursively rewrite a type using `__packed_vec3`, if needed.
-    /// When used as an array element type, the `__packed_vec3` type will be wrapped in a structure
-    /// and given an `@align()` attribute to give it alignment it needs to yield the correct array
-    /// element stride. For vec3 types used in structures directly, the `@align()` attribute is
-    /// placed on the containing structure instead. Matrices with three rows become arrays of
-    /// columns, and used the aligned wrapper struct for the column type.
-    /// @param ty the type to rewrite
-    /// @param array_element `true` if this is being called for the element of an array
-    /// @returns the new AST type, or nullptr if rewriting was not necessary
-    ast::Type RewriteType(const core::type::Type* ty, bool array_element = false) {
-        return Switch(
-            ty,
-            [&](const core::type::Vector* vec) -> ast::Type {
-                if (IsVec3(vec)) {
-                    if (array_element) {
-                        // Create a struct with a single `__packed_vec3` member.
-                        // Give the struct member the same alignment as the original unpacked vec3
-                        // type, to avoid changing the array element stride.
-                        return b.ty(packed_vec3_wrapper_struct_names.GetOrAdd(vec, [&] {
-                            auto name = b.Symbols().New(
-                                "tint_packed_vec3_" + vec->Type()->FriendlyName() +
-                                (array_element ? "_array_element" : "_struct_member"));
-                            auto* member =
-                                b.Member(kStructMemberName, MakePackedVec3(vec),
-                                         tint::Vector{b.MemberAlign(AInt(vec->Align()))});
-                            b.Structure(b.Ident(name), tint::Vector{member}, tint::Empty);
-                            return name;
-                        }));
-                    } else {
-                        return MakePackedVec3(vec);
-                    }
-                }
-                return {};
-            },
-            [&](const core::type::Matrix* mat) -> ast::Type {
-                // Rewrite the matrix as an array of columns that use the aligned wrapper struct.
-                auto new_col_type = RewriteType(mat->ColumnType(), /* array_element */ true);
-                if (new_col_type) {
-                    return b.ty.array(new_col_type, u32(mat->Columns()));
-                }
-                return {};
-            },
-            [&](const core::type::Array* arr) -> ast::Type {
-                // Rewrite the array with the modified element type.
-                auto new_type = RewriteType(arr->ElemType(), /* array_element */ true);
-                if (new_type) {
-                    tint::Vector<const ast::Attribute*, 1> attrs;
-                    if (arr->Count()->Is<core::type::RuntimeArrayCount>()) {
-                        return b.ty.array(new_type, std::move(attrs));
-                    } else if (auto count = arr->ConstantCount()) {
-                        return b.ty.array(new_type, u32(count.value()), std::move(attrs));
-                    } else {
-                        TINT_ICE() << core::type::Array::kErrExpectedConstantCount;
-                    }
-                }
-                return {};
-            },
-            [&](const core::type::Struct* str) -> ast::Type {
-                if (ContainsVec3(str)) {
-                    auto name = rewritten_structs.GetOrAdd(str, [&] {
-                        tint::Vector<const ast::StructMember*, 4> members;
-                        for (auto* member : str->Members()) {
-                            // If the member type contains a vec3, rewrite it.
-                            auto new_type = RewriteType(member->Type());
-                            if (new_type) {
-                                // Copy the member attributes.
-                                bool needs_align = true;
-                                tint::Vector<const ast::Attribute*, 4> attributes;
-                                if (auto* sem_mem = member->As<sem::StructMember>()) {
-                                    for (auto* attr : sem_mem->Declaration()->attributes) {
-                                        if (attr->IsAnyOf<ast::StructMemberAlignAttribute,
-                                                          ast::StructMemberOffsetAttribute>()) {
-                                            needs_align = false;
-                                        }
-                                        attributes.Push(ctx.Clone(attr));
-                                    }
-                                }
-                                // If the alignment wasn't already specified, add an attribute to
-                                // make sure that we don't alter the alignment when using the packed
-                                // vector type.
-                                if (needs_align) {
-                                    attributes.Push(b.MemberAlign(AInt(member->Align())));
-                                }
-                                members.Push(b.Member(ctx.Clone(member->Name()), new_type,
-                                                      std::move(attributes)));
-                            } else {
-                                // No vec3s, just clone the member as is.
-                                if (auto* sem_mem = member->As<sem::StructMember>()) {
-                                    members.Push(ctx.Clone(sem_mem->Declaration()));
-                                } else {
-                                    members.Push(
-                                        b.Member(ctx.Clone(member->Name()), new_type, tint::Empty));
-                                }
-                            }
-                        }
-                        // Create the new structure.
-                        auto struct_name =
-                            b.Symbols().New(str->Name().Name() + "_tint_packed_vec3");
-                        b.Structure(struct_name, std::move(members));
-                        return struct_name;
-                    });
-                    return b.ty(name);
-                }
-                return {};
-            });
-    }
-
-    /// Create a helper function to recursively pack or unpack a composite that contains vec3 types.
-    /// @param name_prefix the name of the helper function
-    /// @param ty the composite type to pack or unpack
-    /// @param pack_or_unpack_element a function that packs or unpacks an element with a given type
-    /// @param in_type a function that create an AST type for the input type
-    /// @param out_type a function that create an AST type for the output type
-    /// @returns the name of the helper function
-    Symbol MakePackUnpackHelper(
-        const char* name_prefix,
-        const core::type::Type* ty,
-        const std::function<const ast::Expression*(const ast::Expression*,
-                                                   const core::type::Type*)>&
-            pack_or_unpack_element,
-        const std::function<ast::Type()>& in_type,
-        const std::function<ast::Type()>& out_type) {
-        // Allocate a variable to hold the return value of the function.
-        tint::Vector<const ast::Statement*, 4> statements;
-
-        // Helper that generates a loop to copy and pack/unpack elements of an array to the result:
-        //   for (var i = 0u; i < num_elements; i = i + 1) {
-        //     result[i] = pack_or_unpack_element(in[i]);
-        //   }
-        auto copy_array_elements = [&](uint32_t num_elements,
-                                       const core::type::Type* element_type) {
-            // Generate code for unpacking the array.
-            if (num_elements <= kMaxSeriallyUnpackedArraySize) {
-                // Generate a variable with an explicit initializer.
-                tint::Vector<const ast::Expression*, 8> elements;
-                for (uint32_t i = 0; i < num_elements; i++) {
-                    elements.Push(pack_or_unpack_element(
-                        b.IndexAccessor("in", b.Expr(core::AInt(i))), element_type));
-                }
-                statements.Push(b.Decl(b.Var("result", b.Call(out_type(), b.ExprList(elements)))));
-            } else {
-                statements.Push(b.Decl(b.Var("result", out_type())));
-                // Generate a for loop.
-                // Generate an expression for packing or unpacking an element of the array.
-                auto* element = pack_or_unpack_element(b.IndexAccessor("in", "i"), element_type);
-                statements.Push(b.For(                   //
-                    b.Decl(b.Var("i", b.ty.u32())),      //
-                    b.LessThan("i", u32(num_elements)),  //
-                    b.Assign("i", b.Add("i", 1_a)),      //
-                    b.Block(tint::Vector{
-                        b.Assign(b.IndexAccessor("result", "i"), element),
-                    })));
-            }
-        };
-
-        // Copy the elements of the value over to the result.
-        Switch(
-            ty,
-            [&](const core::type::Array* arr) {
-                TINT_ASSERT(arr->ConstantCount());
-                copy_array_elements(arr->ConstantCount().value(), arr->ElemType());
-            },
-            [&](const core::type::Matrix* mat) {
-                copy_array_elements(mat->Columns(), mat->ColumnType());
-            },
-            [&](const core::type::Struct* str) {
-                statements.Push(b.Decl(b.Var("result", out_type())));
-                // Copy the struct members over one at a time, packing/unpacking as necessary.
-                for (auto* member : str->Members()) {
-                    const ast::Expression* element =
-                        b.MemberAccessor("in", b.Ident(ctx.Clone(member->Name())));
-                    if (ContainsVec3(member->Type())) {
-                        element = pack_or_unpack_element(element, member->Type());
-                    }
-                    statements.Push(b.Assign(
-                        b.MemberAccessor("result", b.Ident(ctx.Clone(member->Name()))), element));
-                }
-            });
-
-        // Return the result.
-        statements.Push(b.Return("result"));
-
-        // Create the function and return its name.
-        auto name = b.Symbols().New(name_prefix);
-        b.Func(name, tint::Vector{b.Param("in", in_type())}, out_type(), std::move(statements));
-        return name;
-    }
-
-    /// Unpack the composite value `expr` to the unpacked type `ty`. If `ty` is a matrix, this will
-    /// produce a regular matNx3 value from an array of packed column vectors.
-    /// @param expr the composite value expression to unpack
-    /// @param ty the unpacked type
-    /// @returns an expression that holds the unpacked value
-    const ast::Expression* UnpackComposite(const ast::Expression* expr,
-                                           const core::type::Type* ty) {
-        auto helper = unpack_helpers.GetOrAdd(ty, [&] {
-            return MakePackUnpackHelper(
-                "tint_unpack_vec3_in_composite", ty,
-                [&](const ast::Expression* element,
-                    const core::type::Type* element_type) -> const ast::Expression* {
-                    if (element_type->Is<core::type::Vector>()) {
-                        // Unpack a `__packed_vec3` by casting it to a regular vec3.
-                        // If it is an array element, extract the vector from the wrapper struct.
-                        if (element->Is<ast::IndexAccessorExpression>()) {
-                            element = b.MemberAccessor(element, kStructMemberName);
-                        }
-                        return b.Call(CreateASTTypeFor(ctx, element_type), element);
-                    } else {
-                        return UnpackComposite(element, element_type);
-                    }
-                },
-                [&] { return RewriteType(ty); },  //
-                [&] { return CreateASTTypeFor(ctx, ty); });
-        });
-        return b.Call(helper, expr);
-    }
-
-    /// Pack the composite value `expr` from the unpacked type `ty`. If `ty` is a matrix, this will
-    /// produce an array of packed column vectors.
-    /// @param expr the composite value expression to pack
-    /// @param ty the unpacked type
-    /// @returns an expression that holds the packed value
-    const ast::Expression* PackComposite(const ast::Expression* expr, const core::type::Type* ty) {
-        auto helper = pack_helpers.GetOrAdd(ty, [&] {
-            return MakePackUnpackHelper(
-                "tint_pack_vec3_in_composite", ty,
-                [&](const ast::Expression* element,
-                    const core::type::Type* element_type) -> const ast::Expression* {
-                    if (element_type->Is<core::type::Vector>()) {
-                        // Pack a vector element by casting it to a packed_vec3.
-                        // If it is an array element, construct a wrapper struct.
-                        auto* packed = b.Call(MakePackedVec3(element_type), element);
-                        if (element->Is<ast::IndexAccessorExpression>()) {
-                            packed = b.Call(RewriteType(element_type, true), packed);
-                        }
-                        return packed;
-                    } else {
-                        return PackComposite(element, element_type);
-                    }
-                },
-                [&] { return CreateASTTypeFor(ctx, ty); },  //
-                [&] { return RewriteType(ty); });
-        });
-        return b.Call(helper, expr);
-    }
-
-    /// @returns true if there are host-shareable vec3's that need transforming
-    bool ShouldRun() {
-        // Check for vec3s in the types of all uniform and storage buffer variables to determine
-        // if the transform is necessary.
-        for (auto* decl : src.AST().GlobalVariables()) {
-            auto* var = sem.Get<sem::GlobalVariable>(decl);
-            if (var && AddressSpaceNeedsPacking(var->AddressSpace()) &&
-                ContainsVec3(var->Type()->UnwrapRef())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /// Runs the transform
-    /// @returns the new program or SkipTransform if the transform is not required
-    ApplyResult Run() {
-        if (!ShouldRun()) {
-            return SkipTransform;
-        }
-
-        // Changing the types of certain structure members can trigger stricter layout validation
-        // rules for the uniform address space. In particular, replacing 16-bit matrices with arrays
-        // violates the requirement that the array element stride is a multiple of 16 bytes, and
-        // replacing vec3s with a structure violates the requirement that there must be at least 16
-        // bytes from the start of a structure to the start of the next member.
-        // Disable these validation rules using an internal extension, as MSL does not have these
-        // restrictions.
-        b.Enable(wgsl::Extension::kChromiumInternalRelaxedUniformLayout);
-
-        // Track expressions that need to be packed or unpacked.
-        Hashset<const sem::ValueExpression*, 8> to_pack;
-        Hashset<const sem::ValueExpression*, 8> to_unpack;
-
-        // Replace vec3 types in host-shareable address spaces with `__packed_vec3` types, and
-        // collect expressions that need to be converted to or from values that use the
-        // `__packed_vec3` type.
-        for (auto* node : src.ASTNodes().Objects()) {
-            Switch(
-                sem.Get(node),
-                [&](const sem::TypeExpression* type) {
-                    // Rewrite pointers to types that contain vec3s.
-                    auto* ptr = type->Type()->As<core::type::Pointer>();
-                    if (ptr && AddressSpaceNeedsPacking(ptr->AddressSpace())) {
-                        auto new_store_type = RewriteType(ptr->StoreType());
-                        if (new_store_type) {
-                            auto access = ptr->AddressSpace() == core::AddressSpace::kStorage
-                                              ? ptr->Access()
-                                              : core::Access::kUndefined;
-                            auto new_ptr_type =
-                                b.ty.ptr(ptr->AddressSpace(), new_store_type, access);
-                            ctx.Replace(node, new_ptr_type.expr);
-                        }
-                    }
-                },
-                [&](const sem::Variable* var) {
-                    if (!AddressSpaceNeedsPacking(var->AddressSpace())) {
-                        return;
-                    }
-
-                    // Rewrite the var type, if it contains vec3s.
-                    auto new_store_type = RewriteType(var->Type()->UnwrapRef());
-                    if (new_store_type) {
-                        ctx.Replace(var->Declaration()->type.expr, new_store_type.expr);
-                    }
-                },
-                [&](const sem::Statement* stmt) {
-                    // Pack the RHS of assignment statements that are writing to packed types.
-                    if (auto* assign = stmt->Declaration()->As<ast::AssignmentStatement>()) {
-                        auto* lhs = sem.GetVal(assign->lhs);
-                        auto* rhs = sem.GetVal(assign->rhs);
-                        if (!ContainsVec3(rhs->Type()) ||
-                            !AddressSpaceNeedsPacking(
-                                lhs->Type()->As<core::type::Reference>()->AddressSpace())) {
-                            // Skip assignments to address spaces that are not host-shareable, or
-                            // that do not contain vec3 types.
-                            return;
-                        }
-
-                        // Pack the RHS expression.
-                        if (to_unpack.Contains(rhs)) {
-                            // The expression will already be packed, so skip the pending unpack.
-                            to_unpack.Remove(rhs);
-
-                            // If the expression produces a vec3 from an array element, extract
-                            // the packed vector from the wrapper struct.
-                            if (IsVec3(rhs->Type()) &&
-                                rhs->UnwrapLoad()->Is<sem::IndexAccessorExpression>()) {
-                                ctx.Replace(rhs->Declaration(),
-                                            b.MemberAccessor(ctx.Clone(rhs->Declaration()),
-                                                             kStructMemberName));
-                            }
-                        } else if (rhs) {
-                            to_pack.Add(rhs);
-                        }
-                    }
-                },
-                [&](const sem::Load* load) {
-                    // Unpack loads of types that contain vec3s in host-shareable address spaces.
-                    if (ContainsVec3(load->Type()) &&
-                        AddressSpaceNeedsPacking(load->MemoryView()->AddressSpace())) {
-                        to_unpack.Add(load);
-                    }
-                },
-                [&](const sem::IndexAccessorExpression* accessor) {
-                    // If the expression produces a reference to a vec3 in a host-shareable address
-                    // space from an array element, extract the packed vector from the wrapper
-                    // struct.
-                    if (auto* ref = accessor->Type()->As<core::type::Reference>()) {
-                        if (IsVec3(ref->StoreType()) &&
-                            AddressSpaceNeedsPacking(ref->AddressSpace())) {
-                            ctx.Replace(node, b.MemberAccessor(ctx.Clone(accessor->Declaration()),
-                                                               kStructMemberName));
-                        }
-                    }
-                });
-        }
-
-        // Sort the pending pack/unpack operations by AST node ID to make the order deterministic.
-        auto to_unpack_sorted = to_unpack.Vector();
-        auto to_pack_sorted = to_pack.Vector();
-        auto pred = [&](auto* expr_a, auto* expr_b) {
-            return expr_a->Declaration()->node_id < expr_b->Declaration()->node_id;
-        };
-        to_unpack_sorted.Sort(pred);
-        to_pack_sorted.Sort(pred);
-
-        // Apply all of the pending unpack operations that we have collected.
-        for (auto* expr : to_unpack_sorted) {
-            TINT_ASSERT(ContainsVec3(expr->Type()));
-            auto* packed = ctx.Clone(expr->Declaration());
-            const ast::Expression* unpacked = nullptr;
-            if (IsVec3(expr->Type())) {
-                if (expr->UnwrapLoad()->Is<sem::IndexAccessorExpression>()) {
-                    // If we are unpacking a vec3 from an array element, extract the vector from the
-                    // wrapper struct.
-                    packed = b.MemberAccessor(packed, kStructMemberName);
-                }
-                // Cast the packed vector to a regular vec3.
-                unpacked = b.Call(CreateASTTypeFor(ctx, expr->Type()), packed);
-            } else {
-                // Use a helper function to unpack an array or matrix.
-                unpacked = UnpackComposite(packed, expr->Type());
-            }
-            TINT_ASSERT(unpacked != nullptr);
-            ctx.Replace(expr->Declaration(), unpacked);
-        }
-
-        // Apply all of the pending pack operations that we have collected.
-        for (auto* expr : to_pack_sorted) {
-            TINT_ASSERT(ContainsVec3(expr->Type()));
-            auto* unpacked = ctx.Clone(expr->Declaration());
-            const ast::Expression* packed = nullptr;
-            if (IsVec3(expr->Type())) {
-                // Cast the regular vec3 to a packed vector type.
-                packed = b.Call(MakePackedVec3(expr->Type()), unpacked);
-            } else {
-                // Use a helper function to pack an array or matrix.
-                packed = PackComposite(unpacked, expr->Type());
-            }
-            TINT_ASSERT(packed != nullptr);
-            ctx.Replace(expr->Declaration(), packed);
-        }
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-  private:
-    /// The source program
-    const Program& src;
-    /// The target program builder
-    ProgramBuilder b;
-    /// The clone context
-    program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
-    /// Alias to the semantic info in ctx.src
-    const sem::Info& sem = src.Sem();
-};
-
-PackedVec3::PackedVec3() = default;
-PackedVec3::~PackedVec3() = default;
-
-ast::transform::Transform::ApplyResult PackedVec3::Apply(const Program& src,
-                                                         const ast::transform::DataMap&,
-                                                         ast::transform::DataMap&) const {
-    return State{src}.Run();
-}
-
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/packed_vec3.h b/src/tint/lang/msl/writer/ast_raise/packed_vec3.h
deleted file mode 100644
index 9e19dc2..0000000
--- a/src/tint/lang/msl/writer/ast_raise/packed_vec3.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_MSL_WRITER_AST_RAISE_PACKED_VEC3_H_
-#define SRC_TINT_LANG_MSL_WRITER_AST_RAISE_PACKED_VEC3_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::msl::writer {
-
-/// A transform to be used by the MSL backend which will:
-/// * Replace `vec3<T>` types with an internal `__packed_vec3` type when they are used in
-///   host-shareable address spaces.
-/// * Wrap generated `__packed_vec3` types in a structure when they are used in arrays, so that we
-///   ensure that the array has the correct element stride.
-/// * Multi-version structures that contain `vec3<T>` types when they are used in host-shareable
-///   memory, to avoid modifying uses in other address spaces.
-/// * Rewrite matrix types that have three rows into arrays of column vectors.
-/// * Insert calls to helper functions to convert expressions that use these types to or from the
-///   regular vec3 types when accessing host-shareable memory.
-/// * Cast all direct (not sub-accessed) loads of these packed vectors to the 'unpacked' vec3<T>
-///   type before usage.
-///
-/// This transform is necessary in order to emit vec3 types with the correct size (so that scalars
-/// can follow them in structures), and also to ensure that padding bytes are preserved when writing
-/// to a vec3, an array of vec3 elements, or a matrix with vec3 column type.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * ExpandCompoundAssignment
-class PackedVec3 final : public Castable<PackedVec3, ast::transform::Transform> {
-  public:
-    /// Constructor
-    PackedVec3();
-    /// Destructor
-    ~PackedVec3() override;
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::msl::writer
-
-#endif  // SRC_TINT_LANG_MSL_WRITER_AST_RAISE_PACKED_VEC3_H_
diff --git a/src/tint/lang/msl/writer/ast_raise/packed_vec3_test.cc b/src/tint/lang/msl/writer/ast_raise/packed_vec3_test.cc
deleted file mode 100644
index 9fab6df..0000000
--- a/src/tint/lang/msl/writer/ast_raise/packed_vec3_test.cc
+++ /dev/null
@@ -1,8768 +0,0 @@
-// Copyright 2022 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/packed_vec3.h"
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/array.h"
-#include "src/tint/lang/wgsl/ast/module.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/text/string.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::msl::writer {
-namespace {
-
-using PackedVec3Test = ast::transform::TransformTest;
-
-TEST_F(PackedVec3Test, ShouldRun_EmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_NoHostShareableVec3s) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  a : array<vec3<f32>, 4>,
-}
-
-var<private> p_s : S;
-var<private> p_v : vec3<f32>;
-var<private> p_m : mat3x3<f32>;
-var<private> p_a : array<vec3<f32>, 4>;
-
-fn f() {
-  var f_s : S;
-  var f_v : vec3<f32>;
-  var f_m : mat3x3<f32>;
-  var f_a : array<vec3<f32>, 4>;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_Vec4Vec2) {
-    auto* src = R"(
-struct S {
-  v4 : vec4<f32>,
-  v2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> Ps : S; // Host sharable
-@group(0) @binding(1) var<uniform> Pv4 : vec4<f32>; // Host sharable
-@group(0) @binding(2) var<uniform> Pv2 : vec2<f32>; // Host sharable
-)";
-
-    EXPECT_FALSE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_OtherMatrices) {
-    auto* src = R"(
-struct S {
-  m2x2 : mat2x2<f32>,
-  m2x4 : mat2x4<f32>,
-  m3x2 : mat3x2<f32>,
-  m3x4 : mat3x4<f32>,
-  m4x2 : mat4x2<f32>,
-  m4x4 : mat4x4<f32>,
-}
-
-@group(0) @binding(0) var<uniform> Ps : S; // Host sharable
-@group(0) @binding(1) var<uniform> Pm2x2 : mat2x2<f32>; // Host sharable
-@group(0) @binding(2) var<uniform> Pm2x4 : mat2x4<f32>; // Host sharable
-@group(0) @binding(3) var<uniform> Pm3x2 : mat3x2<f32>; // Host sharable
-@group(0) @binding(4) var<uniform> Pm3x4 : mat3x4<f32>; // Host sharable
-@group(0) @binding(5) var<uniform> Pm4x2 : mat4x2<f32>; // Host sharable
-@group(0) @binding(6) var<uniform> Pm4x4 : mat4x4<f32>; // Host sharable
-)";
-
-    EXPECT_FALSE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_ArrayOfNonVec3) {
-    auto* src = R"(
-struct S {
-  arr_v : array<vec2<f32>, 4>,
-  arr_m : array<mat3x2<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> Ps : S; // Host sharable
-@group(0) @binding(1) var<storage> Parr_v : array<vec2<f32>, 4>; // Host sharable
-@group(0) @binding(2) var<storage> Parr_m : array<mat3x2<f32>, 4>; // Host sharable
-)";
-
-    EXPECT_FALSE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_HostSharable_Vec3) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> P : vec3<f32>; // Host sharable
-)";
-
-    EXPECT_TRUE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_HostSharable_Mat3x3) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> P : mat3x3<f32>; // Host sharable
-)";
-
-    EXPECT_TRUE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_HostSharable_ArrayOfVec3) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> P : array<vec3<f32>>; // Host sharable
-)";
-
-    EXPECT_TRUE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_HostSharable_ArrayOfMat3x3) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> P : array<mat3x3<f32>>; // Host sharable
-)";
-
-    EXPECT_TRUE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_HostSharableStruct_Vec3) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<uniform> P : S; // Host sharable
-)";
-
-    EXPECT_TRUE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_HostSharableStruct_Mat3x3) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<uniform> P : S; // Host sharable
-)";
-
-    EXPECT_TRUE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_HostSharableStruct_ArrayOfVec3) {
-    auto* src = R"(
-struct S {
-  a : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<uniform> P : S; // Host sharable
-)";
-
-    EXPECT_TRUE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, ShouldRun_HostSharableStruct_ArrayOfMat3x3) {
-    auto* src = R"(
-struct S {
-  a : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<uniform> P : S; // Host sharable
-)";
-
-    EXPECT_TRUE(ShouldRun<PackedVec3>(src));
-}
-
-TEST_F(PackedVec3Test, Vec3_ReadVector) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> v : vec3<f32>;
-
-fn f() {
-  let x = v;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage> v : __packed_vec3<f32>;
-
-fn f() {
-  let x = vec3<f32>(v);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_ReadComponent_MemberAccessChain) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> v : vec3<f32>;
-
-fn f() {
-  let x = v.yz.x;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage> v : __packed_vec3<f32>;
-
-fn f() {
-  let x = vec3<f32>(v).yz.x;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_ReadComponent_IndexAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> v : vec3<f32>;
-
-fn f() {
-  let x = v[1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage> v : __packed_vec3<f32>;
-
-fn f() {
-  let x = v[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_ReadComponent_IndexAccessor_ViaDerefPointerIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> v : vec3<f32>;
-
-fn f() {
-  let p = &v;
-  let x = (*p)[1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage> v : __packed_vec3<f32>;
-
-fn f() {
-  let p = &(v);
-  let x = (*(p))[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_ReadComponent_IndexAccessor_ViaPointerIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> v : vec3<f32>;
-
-fn f() {
-  let p = &v;
-  let x = p[1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage> v : __packed_vec3<f32>;
-
-fn f() {
-  let p = &(v);
-  let x = p[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_WriteVector_ValueRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-
-fn f() {
-  v = vec3(1.23);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-fn f() {
-  v = __packed_vec3<f32>(vec3(1.22999999999999998224));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_WriteVector_RefRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-@group(0) @binding(1) var<uniform> in : vec3<f32>;
-
-fn f() {
-  v = in;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-@group(0) @binding(1) var<uniform> in : __packed_vec3<f32>;
-
-fn f() {
-  v = in;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_WriteComponent_MemberAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-
-fn f() {
-  v.y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-fn f() {
-  v.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_WriteComponent_MemberAccessor_ViaDerefPointerDot) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-
-fn f() {
-  let p = &v;
-  (*p).y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-fn f() {
-  let p = &(v);
-  (*(p)).y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_WriteComponent_MemberAccessor_ViaPointerDot) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-
-fn f() {
-  let p = &v;
-  p.y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-fn f() {
-  let p = &(v);
-  p.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3_WriteComponent_IndexAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-
-fn f() {
-  v[1] = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-fn f() {
-  v[1] = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_ReadArray_Small) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<vec3<f32>, 4>;
-
-fn f() {
-  let x = arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-@group(0) @binding(0) var<storage> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(arr);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_ReadArray_Large) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<vec3<f32>, 9>;
-
-fn f() {
-  let x = arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 9u>) -> array<vec3<f32>, 9u> {
-  var result : array<vec3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = vec3<f32>(in[i].elements);
-  }
-  return result;
-}
-
-@group(0) @binding(0) var<storage> arr : array<tint_packed_vec3_f32_array_element, 9u>;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(arr);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_ReadVector) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<vec3<f32>, 4>;
-
-fn f() {
-  let x = arr[0];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-fn f() {
-  let x = vec3<f32>(arr[0].elements);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_ReadComponent_MemberAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<vec3<f32>, 4>;
-
-fn f() {
-  let x = arr[0].y;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-fn f() {
-  let x = arr[0].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_ReadComponent_IndexAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<vec3<f32>, 4>;
-
-fn f() {
-  let x = arr[0][1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-fn f() {
-  let x = arr[0].elements[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_WriteArray_ValueRHS_Small) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<vec3<f32>, 2>;
-
-fn f() {
-  arr = array(vec3(1.5, 2.5, 3.5), vec3(4.5, 5.5, 6.5));
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 2u>) -> array<tint_packed_vec3_f32_array_element, 2u> {
-  var result = array<tint_packed_vec3_f32_array_element, 2u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])));
-  return result;
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 2u>;
-
-fn f() {
-  arr = tint_pack_vec3_in_composite(array(vec3(1.5, 2.5, 3.5), vec3(4.5, 5.5, 6.5)));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_WriteArray_ValueRHS_Large) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<vec3<f32>, 9>;
-
-fn f() {
-  arr = array(vec3(1.5, 2.5, 3.5),
-              vec3(4.5, 5.5, 6.5),
-              vec3(7.5, 8.5, 9.5),
-              vec3(7.5, 8.5, 9.5),
-              vec3(7.5, 8.5, 9.5),
-              vec3(7.5, 8.5, 9.5),
-              vec3(7.5, 8.5, 9.5),
-              vec3(7.5, 8.5, 9.5),
-              vec3(7.5, 8.5, 9.5));
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 9u>) -> array<tint_packed_vec3_f32_array_element, 9u> {
-  var result : array<tint_packed_vec3_f32_array_element, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[i]));
-  }
-  return result;
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 9u>;
-
-fn f() {
-  arr = tint_pack_vec3_in_composite(array(vec3(1.5, 2.5, 3.5), vec3(4.5, 5.5, 6.5), vec3(7.5, 8.5, 9.5), vec3(7.5, 8.5, 9.5), vec3(7.5, 8.5, 9.5), vec3(7.5, 8.5, 9.5), vec3(7.5, 8.5, 9.5), vec3(7.5, 8.5, 9.5), vec3(7.5, 8.5, 9.5)));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_WriteArray_RefRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<vec3<f32>, 2>;
-@group(0) @binding(1) var<uniform> in : array<vec3<f32>, 2>;
-
-fn f() {
-  arr = in;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 2u>;
-
-@group(0) @binding(1) var<uniform> in : array<tint_packed_vec3_f32_array_element, 2u>;
-
-fn f() {
-  arr = in;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_WriteVector_ValueRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<vec3<f32>, 4>;
-
-fn f() {
-  arr[0] = vec3(1.23);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-fn f() {
-  arr[0].elements = __packed_vec3<f32>(vec3(1.22999999999999998224));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_WriteVector_RefRHS_Small) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<vec3<f32>, 4>;
-@group(0) @binding(1) var<uniform> in_arr : array<vec3<f32>, 4>;
-@group(0) @binding(2) var<uniform> in_vec : vec3<f32>;
-
-fn f() {
-  arr[0] = in_arr[0];
-  arr[1] = in_vec;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(1) var<uniform> in_arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(2) var<uniform> in_vec : __packed_vec3<f32>;
-
-fn f() {
-  arr[0].elements = in_arr[0].elements;
-  arr[1].elements = in_vec;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_WriteVector_RefRHS_Large) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<vec3<f32>, 9>;
-@group(0) @binding(1) var<uniform> in_arr : array<vec3<f32>, 9>;
-@group(0) @binding(2) var<uniform> in_vec : vec3<f32>;
-
-fn f() {
-  arr[0] = in_arr[0];
-  arr[1] = in_vec;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 9u>;
-
-@group(0) @binding(1) var<uniform> in_arr : array<tint_packed_vec3_f32_array_element, 9u>;
-
-@group(0) @binding(2) var<uniform> in_vec : __packed_vec3<f32>;
-
-fn f() {
-  arr[0].elements = in_arr[0].elements;
-  arr[1].elements = in_vec;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_WriteComponent_MemberAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<vec3<f32>, 4>;
-
-fn f() {
-  arr[0].y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-fn f() {
-  arr[0].elements.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3_WriteComponent_IndexAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<vec3<f32>, 4>;
-
-fn f() {
-  arr[0][1] = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-fn f() {
-  arr[0].elements[1] = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_ReadMatrix) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> m : mat3x3<f32>;
-
-fn f() {
-  let x = m;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-@group(0) @binding(0) var<storage> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(m);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_ReadColumn) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> m : mat3x3<f32>;
-
-fn f() {
-  let x = m[1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  let x = vec3<f32>(m[1].elements);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_ReadComponent_MemberAccessChain) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> m : mat3x3<f32>;
-
-fn f() {
-  let x = m[1].yz.x;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  let x = vec3<f32>(m[1].elements).yz.x;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_ReadComponent_IndexAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> m : mat3x3<f32>;
-
-fn f() {
-  let x = m[2][1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  let x = m[2].elements[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_WriteMatrix_ValueRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
-
-fn f() {
-  m = mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-@group(0) @binding(0) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  m = tint_pack_vec3_in_composite(mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_WriteMatrix_RefRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
-@group(0) @binding(1) var<uniform> in : mat3x3<f32>;
-
-fn f() {
-  m = in;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(1) var<uniform> in : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  m = in;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_WriteColumn_ValueRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
-
-fn f() {
-  m[1] = vec3(1.23);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  m[1].elements = __packed_vec3<f32>(vec3(1.22999999999999998224));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_WriteColumn_RefRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
-@group(0) @binding(1) var<uniform> in_mat : mat3x3<f32>;
-@group(0) @binding(1) var<uniform> in_vec : vec3<f32>;
-
-fn f() {
-  m[0] = in_mat[0];
-  m[1] = in_vec;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(1) var<uniform> in_mat : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(1) var<uniform> in_vec : __packed_vec3<f32>;
-
-fn f() {
-  m[0].elements = in_mat[0].elements;
-  m[1].elements = in_vec;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_WriteComponent_MemberAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
-
-fn f() {
-  m[1].y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  m[1].elements.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Matrix_WriteComponent_IndexAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
-
-fn f() {
-  m[1][2] = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  m[1].elements[2] = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_ReadArray_Small) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  let x = arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-@group(0) @binding(0) var<storage> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_1(arr);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_ReadArray_Large) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<mat3x3<f32>, 9>;
-
-fn f() {
-  let x = arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>) -> array<mat3x3<f32>, 9u> {
-  var result : array<mat3x3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite(in[i]);
-  }
-  return result;
-}
-
-@group(0) @binding(0) var<storage> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_1(arr);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_ReadMatrix) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  let x = arr[0];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-@group(0) @binding(0) var<storage> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(arr[0]);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_ReadColumn) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  let x = arr[0][1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  let x = vec3<f32>(arr[0][1].elements);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_ReadComponent_MemberAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  let x = arr[0][1].y;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  let x = arr[0][1].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_ReadComponent_IndexAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  let x = arr[0][1][2];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  let x = arr[0][1].elements[2];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteArray_ValueRHS_Small) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 2>;
-
-fn f() {
-  arr = array(mat3x3<f32>(), mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5));
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<mat3x3<f32>, 2u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 2u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]));
-  return result;
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>;
-
-fn f() {
-  arr = tint_pack_vec3_in_composite_1(array(mat3x3<f32>(), mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5)));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteArray_ValueRHS_Large) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 9>;
-
-fn f() {
-  arr = array(mat3x3<f32>(),
-              mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5),
-              mat3x3f(),
-              mat3x3f(),
-              mat3x3f(),
-              mat3x3f(),
-              mat3x3f(),
-              mat3x3f(),
-              mat3x3f());
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<mat3x3<f32>, 9u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 9u> {
-  var result : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_pack_vec3_in_composite(in[i]);
-  }
-  return result;
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>;
-
-fn f() {
-  arr = tint_pack_vec3_in_composite_1(array(mat3x3<f32>(), mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5), mat3x3f(), mat3x3f(), mat3x3f(), mat3x3f(), mat3x3f(), mat3x3f(), mat3x3f()));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteArray_RefRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 2>;
-@group(0) @binding(1) var<uniform> in : array<mat3x3<f32>, 2>;
-
-fn f() {
-  arr = in;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>;
-
-@group(0) @binding(1) var<uniform> in : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>;
-
-fn f() {
-  arr = in;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteMatrix_ValueRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  arr[0] = mat3x3(1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  arr[0] = tint_pack_vec3_in_composite(mat3x3(1.10000000000000008882, 2.20000000000000017764, 3.29999999999999982236, 4.40000000000000035527, 5.5, 6.59999999999999964473, 7.70000000000000017764, 8.80000000000000071054, 9.90000000000000035527));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteMatrix_RefRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 4>;
-@group(0) @binding(1) var<uniform> in_arr : array<mat3x3<f32>, 4>;
-@group(0) @binding(2) var<uniform> in_mat : mat3x3<f32>;
-
-fn f() {
-  arr[0] = in_arr[0];
-  arr[1] = in_mat;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(1) var<uniform> in_arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(2) var<uniform> in_mat : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  arr[0] = in_arr[0];
-  arr[1] = in_mat;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteVector_ValueRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  arr[0][1] = vec3(1.23);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  arr[0][1].elements = __packed_vec3<f32>(vec3(1.22999999999999998224));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteVector_RefRHS) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 4>;
-@group(0) @binding(1) var<uniform> in_arr : array<mat3x3<f32>, 4>;
-@group(0) @binding(2) var<uniform> in_mat : mat3x3<f32>;
-@group(0) @binding(3) var<uniform> in_vec : vec3<f32>;
-
-fn f() {
-  arr[0][0] = arr[0][1];
-  arr[0][1] = in_mat[2];
-  arr[0][2] = in_vec;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(1) var<uniform> in_arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(2) var<uniform> in_mat : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(3) var<uniform> in_vec : __packed_vec3<f32>;
-
-fn f() {
-  arr[0][0].elements = arr[0][1].elements;
-  arr[0][1].elements = in_mat[2].elements;
-  arr[0][2].elements = in_vec;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteComponent_MemberAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  arr[0][1].y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  arr[0][1].elements.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrix_WriteComponent_IndexAccessor) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> arr : array<mat3x3<f32>, 4>;
-
-fn f() {
-  arr[0][1][2] = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-fn f() {
-  arr[0][1].elements[2] = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_ReadStruct) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-fn tint_unpack_vec3_in_composite(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.v = vec3<f32>(in.v);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(P);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_ReadVector) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.v;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = vec3<f32>(P.v);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_ReadComponent_MemberAccessChain) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.v.yz.x;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = vec3<f32>(P.v).yz.x;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_ReadComponent_IndexAccessor) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.v[1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.v[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_WriteStruct_ValueRHS) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P = S(vec3(1.23));
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-fn tint_pack_vec3_in_composite(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.v = __packed_vec3<f32>(in.v);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P = tint_pack_vec3_in_composite(S(vec3(1.22999999999999998224)));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_WriteStruct_RefRHS) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in : S;
-
-fn f() {
-  P = in;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in : S_tint_packed_vec3;
-
-fn f() {
-  P = in;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_WriteVector_ValueRHS) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.v = vec3(1.23);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.v = __packed_vec3<f32>(vec3(1.22999999999999998224));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_WriteVector_RefRHS) {
-    auto* src = R"(
-struct S {
-  v1 : vec3<f32>,
-  v2 : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in_str : S;
-@group(0) @binding(2) var<uniform> in_vec : vec3<f32>;
-
-fn f() {
-  P.v1 = in_str.v1;
-  P.v2 = in_vec;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v1 : __packed_vec3<f32>,
-  @align(16)
-  v2 : __packed_vec3<f32>,
-}
-
-struct S {
-  v1 : vec3<f32>,
-  v2 : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in_str : S_tint_packed_vec3;
-
-@group(0) @binding(2) var<uniform> in_vec : __packed_vec3<f32>;
-
-fn f() {
-  P.v1 = in_str.v1;
-  P.v2 = in_vec;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_WriteComponent_MemberAccessor) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.v.y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.v.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Vec3_WriteComponent_IndexAccessor) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.v[1] = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.v[1] = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_ReadStruct_Small) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr = tint_unpack_vec3_in_composite(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_1(P);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_ReadStruct_Large) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 9u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 9u>) -> array<vec3<f32>, 9u> {
-  var result : array<vec3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = vec3<f32>(in[i].elements);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr = tint_unpack_vec3_in_composite(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_1(P);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_ReadArray_Small) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(P.arr);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_ReadArray_Large) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 9u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 9u>) -> array<vec3<f32>, 9u> {
-  var result : array<vec3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = vec3<f32>(in[i].elements);
-  }
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(P.arr);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_ReadVector) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr[0];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = vec3<f32>(P.arr[0].elements);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_ReadComponent_MemberAccessor) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr[0].y;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.arr[0].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_ReadComponent_IndexAccessor) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr[0][1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.arr[0].elements[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteStruct_ValueRHS_Small) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P = S(array(vec3(1.5, 4.5, 7.5), vec3(9.5, 6.5, 3.5)));
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 2u>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 2u>) -> array<tint_packed_vec3_f32_array_element, 2u> {
-  var result = array<tint_packed_vec3_f32_array_element, 2u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.arr = tint_pack_vec3_in_composite(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P = tint_pack_vec3_in_composite_1(S(array(vec3(1.5, 4.5, 7.5), vec3(9.5, 6.5, 3.5))));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteStruct_ValueRHS_Large) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P = S(array(vec3(1.5, 4.5, 7.5),
-              vec3(9.5, 6.5, 3.5),
-              vec3(9.5, 6.5, 3.5),
-              vec3(9.5, 6.5, 3.5),
-              vec3(9.5, 6.5, 3.5),
-              vec3(9.5, 6.5, 3.5),
-              vec3(9.5, 6.5, 3.5),
-              vec3(9.5, 6.5, 3.5),
-              vec3(9.5, 6.5, 3.5)));
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 9u>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 9u>) -> array<tint_packed_vec3_f32_array_element, 9u> {
-  var result : array<tint_packed_vec3_f32_array_element, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[i]));
-  }
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.arr = tint_pack_vec3_in_composite(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P = tint_pack_vec3_in_composite_1(S(array(vec3(1.5, 4.5, 7.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5))));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteStruct_RefRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in : S;
-
-fn f() {
-  P = in;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 2u>,
-}
-
-struct S {
-  arr : array<vec3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in : S_tint_packed_vec3;
-
-fn f() {
-  P = in;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteArray_ValueRHS_Small) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr = array(vec3(1.5, 4.5, 7.5), vec3(9.5, 6.5, 3.5));
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 2u>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 2u>) -> array<tint_packed_vec3_f32_array_element, 2u> {
-  var result = array<tint_packed_vec3_f32_array_element, 2u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])));
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr = tint_pack_vec3_in_composite(array(vec3(1.5, 4.5, 7.5), vec3(9.5, 6.5, 3.5)));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteArray_ValueRHS_Large) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr = array(vec3(1.5, 4.5, 7.5),
-                vec3(9.5, 6.5, 3.5),
-                vec3(9.5, 6.5, 3.5),
-                vec3(9.5, 6.5, 3.5),
-                vec3(9.5, 6.5, 3.5),
-                vec3(9.5, 6.5, 3.5),
-                vec3(9.5, 6.5, 3.5),
-                vec3(9.5, 6.5, 3.5),
-                vec3(9.5, 6.5, 3.5));
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 9u>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 9u>) -> array<tint_packed_vec3_f32_array_element, 9u> {
-  var result : array<tint_packed_vec3_f32_array_element, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[i]));
-  }
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr = tint_pack_vec3_in_composite(array(vec3(1.5, 4.5, 7.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5), vec3(9.5, 6.5, 3.5)));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteArray_RefRHS) {
-    auto* src = R"(
-struct S {
-  arr1 : array<vec3<f32>, 2>,
-  arr2 : array<vec3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in_str : S;
-@group(0) @binding(2) var<uniform> in_arr : array<vec3<f32>, 2>;
-
-fn f() {
-  P.arr1 = in_str.arr1;
-  P.arr2 = in_arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr1 : array<tint_packed_vec3_f32_array_element, 2u>,
-  @align(16)
-  arr2 : array<tint_packed_vec3_f32_array_element, 2u>,
-}
-
-struct S {
-  arr1 : array<vec3<f32>, 2>,
-  arr2 : array<vec3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in_str : S_tint_packed_vec3;
-
-@group(0) @binding(2) var<uniform> in_arr : array<tint_packed_vec3_f32_array_element, 2u>;
-
-fn f() {
-  P.arr1 = in_str.arr1;
-  P.arr2 = in_arr;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteVector_ValueRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr[0] = vec3(1.23);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr[0].elements = __packed_vec3<f32>(vec3(1.22999999999999998224));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteVector_RefRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in_str : S;
-@group(0) @binding(2) var<uniform> in_arr : array<vec3<f32>, 4>;
-@group(0) @binding(3) var<uniform> in_vec : vec3<f32>;
-
-fn f() {
-  P.arr[0] = in_str.arr[0];
-  P.arr[1] = in_arr[1];
-  P.arr[2] = in_vec;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in_str : S_tint_packed_vec3;
-
-@group(0) @binding(2) var<uniform> in_arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(3) var<uniform> in_vec : __packed_vec3<f32>;
-
-fn f() {
-  P.arr[0].elements = in_str.arr[0].elements;
-  P.arr[1].elements = in_arr[1].elements;
-  P.arr[2].elements = in_vec;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteComponent_MemberAccessor) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr[0].y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr[0].elements.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfVec3_WriteComponent_IndexAccessor) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr[0][1] = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr[0].elements[1] = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_ReadStruct) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.m = tint_unpack_vec3_in_composite(in.m);
-  return result;
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_1(P);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_ReadMatrix) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.m;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(P.m);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_ReadColumn) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.m[1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = vec3<f32>(P.m[1].elements);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_ReadComponent_MemberAccessChain) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.m[1].yz.x;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = vec3<f32>(P.m[1].elements).yz.x;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_ReadComponent_IndexAccessor) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.m[2][1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.m[2].elements[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_WriteStruct_ValueRHS) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P = S(mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5));
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.m = tint_pack_vec3_in_composite(in.m);
-  return result;
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P = tint_pack_vec3_in_composite_1(S(mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5)));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_WriteStruct_RefRHS) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in : S;
-
-fn f() {
-  P = in;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in : S_tint_packed_vec3;
-
-fn f() {
-  P = in;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_WriteMatrix_ValueRHS) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.m = mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.m = tint_pack_vec3_in_composite(mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_WriteMatrix_RefRHS) {
-    auto* src = R"(
-struct S {
-  m1 : mat3x3<f32>,
-  m2 : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in_str : S;
-@group(0) @binding(2) var<uniform> in_mat : mat3x3<f32>;
-
-fn f() {
-  P.m1 = in_str.m1;
-  P.m2 = in_mat;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m1 : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  m2 : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m1 : mat3x3<f32>,
-  m2 : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in_str : S_tint_packed_vec3;
-
-@group(0) @binding(2) var<uniform> in_mat : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  P.m1 = in_str.m1;
-  P.m2 = in_mat;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_WriteColumn_ValueRHS) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.m[1] = vec3(1.23);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.m[1].elements = __packed_vec3<f32>(vec3(1.22999999999999998224));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_WriteColumn_RefRHS) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in_str : S;
-@group(0) @binding(2) var<uniform> in_mat : mat3x3<f32>;
-@group(0) @binding(3) var<uniform> in_vec : vec3<f32>;
-
-fn f() {
-  P.m[0] = in_str.m[0];
-  P.m[1] = in_mat[1];
-  P.m[2] = in_vec;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in_str : S_tint_packed_vec3;
-
-@group(0) @binding(2) var<uniform> in_mat : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(3) var<uniform> in_vec : __packed_vec3<f32>;
-
-fn f() {
-  P.m[0].elements = in_str.m[0].elements;
-  P.m[1].elements = in_mat[1].elements;
-  P.m[2].elements = in_vec;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_WriteComponent_MemberAccessor) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.m[1].y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.m[1].elements.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_Matrix_WriteComponent_IndexAccessor) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.m[1][2] = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.m[1].elements[2] = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_ReadStruct_Small) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr = tint_unpack_vec3_in_composite_1(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_2(P);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_ReadStruct_Large) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>) -> array<mat3x3<f32>, 9u> {
-  var result : array<mat3x3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite(in[i]);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr = tint_unpack_vec3_in_composite_1(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_2(P);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_ReadArray) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>) -> array<mat3x3<f32>, 9u> {
-  var result : array<mat3x3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite(in[i]);
-  }
-  return result;
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 9>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_1(P.arr);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_ReadMatrix) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr[0];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite(P.arr[0]);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_ReadColumn) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr[0][1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = vec3<f32>(P.arr[0][1].elements);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_ReadComponent_MemberAccessor) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr[0][1].y;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.arr[0][1].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_ReadComponent_IndexAccessor) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let x = P.arr[0][1][2];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.arr[0][1].elements[2];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteStruct_ValueRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P = S(array(mat3x3<f32>(), mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5)));
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<mat3x3<f32>, 2u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 2u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_2(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.arr = tint_pack_vec3_in_composite_1(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P = tint_pack_vec3_in_composite_2(S(array(mat3x3<f32>(), mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5))));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteStruct_RefRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in : S;
-
-fn f() {
-  P = in;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in : S_tint_packed_vec3;
-
-fn f() {
-  P = in;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteArray_ValueRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr = array(mat3x3<f32>(), mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5));
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<mat3x3<f32>, 2u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 2u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]));
-  return result;
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr = tint_pack_vec3_in_composite_1(array(mat3x3<f32>(), mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5)));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteArray_RefRHS) {
-    auto* src = R"(
-struct S {
-  arr1 : array<mat3x3<f32>, 2>,
-  arr2 : array<mat3x3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in_str : S;
-@group(0) @binding(2) var<uniform> in_arr : array<mat3x3<f32>, 2>;
-
-fn f() {
-  P.arr1 = in_str.arr1;
-  P.arr2 = in_arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr1 : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>,
-  @align(16)
-  arr2 : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>,
-}
-
-struct S {
-  arr1 : array<mat3x3<f32>, 2>,
-  arr2 : array<mat3x3<f32>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in_str : S_tint_packed_vec3;
-
-@group(0) @binding(2) var<uniform> in_arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 2u>;
-
-fn f() {
-  P.arr1 = in_str.arr1;
-  P.arr2 = in_arr;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteMatrix_ValueRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr[0] = mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr[0] = tint_pack_vec3_in_composite(mat3x3(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteMatrix_RefRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in_str : S;
-@group(0) @binding(2) var<uniform> in_arr : array<mat3x3<f32>, 4>;
-@group(0) @binding(3) var<uniform> in_mat : mat3x3<f32>;
-
-fn f() {
-  P.arr[0] = in_str.arr[0];
-  P.arr[1] = in_arr[1];
-  P.arr[2] = in_mat;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in_str : S_tint_packed_vec3;
-
-@group(0) @binding(2) var<uniform> in_arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(3) var<uniform> in_mat : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  P.arr[0] = in_str.arr[0];
-  P.arr[1] = in_arr[1];
-  P.arr[2] = in_mat;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteVector_ValueRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr[0][1] = vec3(1.23);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr[0][1].elements = __packed_vec3<f32>(vec3(1.22999999999999998224));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteVector_RefRHS) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-@group(0) @binding(1) var<uniform> in_str : S;
-@group(0) @binding(2) var<uniform> in_arr : array<mat3x3<f32>, 4>;
-@group(0) @binding(3) var<uniform> in_mat : mat3x3<f32>;
-@group(0) @binding(4) var<uniform> in_vec : vec3<f32>;
-
-fn f() {
-  P.arr[0][0] = in_str.arr[0][1];
-  P.arr[1][1] = in_arr[3][2];
-  P.arr[2][2] = in_mat[1];
-  P.arr[3][0] = in_vec;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<uniform> in_str : S_tint_packed_vec3;
-
-@group(0) @binding(2) var<uniform> in_arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(3) var<uniform> in_mat : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(4) var<uniform> in_vec : __packed_vec3<f32>;
-
-fn f() {
-  P.arr[0][0].elements = in_str.arr[0][1].elements;
-  P.arr[1][1].elements = in_arr[3][2].elements;
-  P.arr[2][2].elements = in_mat[1].elements;
-  P.arr[3][0].elements = in_vec;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteComponent_MemberAccessor) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr[0][1].y = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr[0][1].elements.y = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ArrayOfMatrix_WriteComponent_IndexAccessor) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  P.arr[0][1][2] = 1.23;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4u>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  P.arr[0][1].elements[2] = 1.22999999999999998224;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ExistingMemberAttributes) {
-    auto* src = R"(
-struct S {
-  @align(32) @size(32) v : vec3<f32>,
-  @align(64) @size(64) arr : array<vec3<f32>, 4>,
-  @align(128) @size(128) x : u32,
-}
-
-@group(0) @binding(0) var<uniform> P : S;
-
-fn f() {
-  let x = P.v[0];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(32) @size(32)
-  v : __packed_vec3<f32>,
-  @align(64) @size(64)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(128) @size(128)
-  x : u32,
-}
-
-struct S {
-  @align(32) @size(32)
-  v : vec3<f32>,
-  @align(64) @size(64)
-  arr : array<vec3<f32>, 4>,
-  @align(128) @size(128)
-  x : u32,
-}
-
-@group(0) @binding(0) var<uniform> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.v[0];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ExistingMemberAttributes_SizeMatchesUnpackedVec3) {
-    // Test that the type we replace a vec3 with is not larger than it should be.
-    auto* src = R"(
-struct S {
-  @size(12) v : vec3<f32>,
-  @size(64) arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<uniform> P : S;
-
-fn f() {
-  let x = P.v[0];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @size(12) @align(16)
-  v : __packed_vec3<f32>,
-  @size(64) @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  @size(12)
-  v : vec3<f32>,
-  @size(64)
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<uniform> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.v[0];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ExistingMemberAttributes_AlignTooSmall) {
-    // Test that we add an @align() attribute when the new alignment of the packed vec3 struct would
-    // be too small.
-    auto* src = R"(
-struct S {
-  a : u32,
-  v : vec3<f32>,
-  b : u32,
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<uniform> P : S;
-
-fn f() {
-  let x = P.v[0];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  a : u32,
-  @align(16)
-  v : __packed_vec3<f32>,
-  b : u32,
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  a : u32,
-  v : vec3<f32>,
-  b : u32,
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<uniform> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = P.v[0];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructMember_ExistingMemberAttributes_ExplicitOffset) {
-    // Test that the we do not add an @align attribute if @offset is present.
-
-    // struct S {
-    //   a : u32,
-    //   @offset(32) v : vec3<f32>,
-    //   b : u32,
-    //   @offset(128) arr : array<vec3<f32>, 4>,
-    // }
-    //
-    // @group(0) @binding(0) var<uniform> P : S;
-    ProgramBuilder b;
-    b.Structure("S", tint::Vector{
-                         b.Member("a", b.ty.u32()),
-                         b.Member("v", b.ty.vec3<f32>(), tint::Vector{b.MemberOffset(AInt(32))}),
-                         b.Member("b", b.ty.u32()),
-                         b.Member("arr", b.ty.array(b.ty.vec3<f32>(), b.Expr(AInt(4))),
-                                  tint::Vector{b.MemberOffset(AInt(128))}),
-                     });
-    b.GlobalVar("P", core::AddressSpace::kStorage, b.ty("S"),
-                tint::Vector{b.Group(AInt(0)), b.Binding(AInt(0))});
-    Program src(resolver::Resolve(b));
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  a : u32,
-  @size(28)
-  padding_0 : u32,
-  /* @offset(32) */
-  v : __packed_vec3<f32>,
-  b : u32,
-  @size(80)
-  padding_1 : u32,
-  /* @offset(128) */
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  a : u32,
-  @size(16)
-  padding_0 : u32,
-  /* @offset(32) */
-  v : vec3<f32>,
-  b : u32,
-  @size(80)
-  padding_1 : u32,
-  /* @offset(128) */
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(std::move(src), data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructValueConstructor_ViaIndexAccessor) {
-    auto* src = R"(
-struct S {
-  a : vec3<f32>,
-  b : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> s : S;
-
-fn f() {
-  let value_arr : array<vec3<f32>, 4> = array<vec3<f32>, 4>();
-  let x = S(value_arr[0], s.arr[0], value_arr);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  a : __packed_vec3<f32>,
-  @align(16)
-  b : __packed_vec3<f32>,
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct S {
-  a : vec3<f32>,
-  b : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> s : S_tint_packed_vec3;
-
-fn f() {
-  let value_arr : array<vec3<f32>, 4> = array<vec3<f32>, 4>();
-  let x = S(value_arr[0], vec3<f32>(s.arr[0].elements), value_arr);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, WrapperStructLayout_MixedUsage) {
-    // Test the layout of the generated wrapper struct(s) when vec3s are used in both structures and
-    // arrays.
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  a : u32,
-}
-
-@group(0) @binding(0) var<storage, read_write> str : S;
-@group(0) @binding(1) var<storage, read_write> arr : array<vec3<f32>, 4>;
-
-fn main() {
-  str.v = arr[0];
-  arr[1] = str.v;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  a : u32,
-}
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-  a : u32,
-}
-
-@group(0) @binding(0) var<storage, read_write> str : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<storage, read_write> arr : array<tint_packed_vec3_f32_array_element, 4u>;
-
-fn main() {
-  str.v = arr[0].elements;
-  arr[1].elements = str.v;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    auto& vars = got.program.AST().GlobalVariables();
-    ASSERT_EQ(vars.Length(), 2u);
-
-    {
-        // Check the layout of the struct type of "str".
-        // The first member should have an alignment of 16 bytes, a size of 12 bytes, and the second
-        // member should have an offset of 12 bytes.
-        auto* sem_str = got.program.Sem().Get(vars[0]);
-        auto* str_ty = sem_str->Type()->UnwrapRef()->As<core::type::Struct>();
-        ASSERT_NE(str_ty, nullptr);
-        ASSERT_EQ(str_ty->Members().Length(), 2u);
-        EXPECT_EQ(str_ty->Members()[0]->Align(), 16u);
-        EXPECT_EQ(str_ty->Members()[0]->Size(), 12u);
-        EXPECT_EQ(str_ty->Members()[1]->Offset(), 12u);
-    }
-
-    {
-        // Check the layout of the array type of "arr".
-        // The element stride should be 16 bytes.
-        auto* sem_arr = got.program.Sem().Get(vars[1]);
-        auto* arr_ty = sem_arr->Type()->UnwrapRef()->As<core::type::Array>();
-        ASSERT_NE(arr_ty, nullptr);
-        EXPECT_EQ(arr_ty->Stride(), 16u);
-    }
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, PackUnpackStructWithNonVec3Members) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-  a : u32,
-  b : vec4<f32>,
-  c : array<vec4<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  let x = P;
-  P = x;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-  a : u32,
-  b : vec4<f32>,
-  c : array<vec4<f32>, 4>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.v = vec3<f32>(in.v);
-  result.arr = tint_unpack_vec3_in_composite(in.arr);
-  result.a = in.a;
-  result.b = in.b;
-  result.c = in.c;
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.v = __packed_vec3<f32>(in.v);
-  result.arr = tint_pack_vec3_in_composite(in.arr);
-  result.a = in.a;
-  result.b = in.b;
-  result.c = in.c;
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-  a : u32,
-  b : vec4<f32>,
-  c : array<vec4<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  let x = tint_unpack_vec3_in_composite_1(P);
-  P = tint_pack_vec3_in_composite_1(x);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Struct_ShaderIO) {
-    // Test that we do not modify structures that are used for shader IO.
-    auto* src = R"(
-struct S1 {
-  @location(0) v : vec3<f32>,
-}
-
-struct S2 {
-  @location(0) v : vec3<f32>,
-  @builtin(position) pos : vec4<f32>,
-}
-
-@vertex
-fn main(s1 : S1) -> S2 {
-  let v : vec3<f32> = s1.v;
-  var s2 : S2;
-  s2.v = v;
-  return s2;
-}
-)";
-
-    auto* expect = R"(
-struct S1 {
-  @location(0)
-  v : vec3<f32>,
-}
-
-struct S2 {
-  @location(0)
-  v : vec3<f32>,
-  @builtin(position)
-  pos : vec4<f32>,
-}
-
-@vertex
-fn main(s1 : S1) -> S2 {
-  let v : vec3<f32> = s1.v;
-  var s2 : S2;
-  s2.v = v;
-  return s2;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ModfReturnStruct) {
-    // Test that we do not try to modify accessors on the anonymous structure returned by modf.
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> output : vec3<f32>;
-
-const values = array(modf(vec3(1.0, 2.0, 3.0)).fract);
-
-@compute @workgroup_size(1)
-fn main() {
-  output = values[0];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> output : __packed_vec3<f32>;
-
-const values = array(modf(vec3(1.0, 2.0, 3.0)).fract);
-
-@compute @workgroup_size(1)
-fn main() {
-  output = __packed_vec3<f32>(values[0]);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ModfReturnStruct_PointerToMember) {
-    // Test that we can pass a pointer to the vec3 member of the modf return struct to a function
-    // parameter to which we also pass a pointer to a vec3 member on a host-shareable struct.
-    auto* src = R"(
-struct S {
-  v : vec3<f32>
-}
-
-@group(0) @binding(0) var<storage, read_write> output : S;
-
-fn foo(p : ptr<function, vec3<f32>>) {
-  (*p) = vec3(1, 2, 3);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var f : S;
-  var modf_ret = modf(vec3(1.0, 2.0, 3.0));
-  foo(&f.v);
-  foo(&modf_ret.fract);
-  output.v = modf_ret.fract;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> output : S_tint_packed_vec3;
-
-fn foo(p : ptr<function, vec3<f32>>) {
-  *(p) = vec3(1, 2, 3);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var f : S;
-  var modf_ret = modf(vec3(1.0, 2.0, 3.0));
-  foo(&(f.v));
-  foo(&(modf_ret.fract));
-  output.v = __packed_vec3<f32>(modf_ret.fract);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MultipleStructMembers) {
-    auto* src = R"(
-struct S {
-  v2_a : vec2<f32>,
-  v3_a : vec3<f32>,
-  v4_a : vec4<f32>,
-  v2_b : vec2<f32>,
-  v3_b : vec3<f32>,
-  v4_b : vec4<f32>,
-  v2_arr : array<vec2<f32>, 4>,
-  v3_arr : array<vec3<f32>, 4>,
-  v4_arr : array<vec4<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  let v2_a = P.v2_a;
-  let v3_a = P.v3_a;
-  let v4_a = P.v4_a;
-  let v2_b = P.v2_b;
-  let v3_b = P.v3_b;
-  let v4_b = P.v4_b;
-  let v2_arr : array<vec2<f32>, 4> = P.v2_arr;
-  let v3_arr : array<vec3<f32>, 4> = P.v3_arr;
-  let v4_arr : array<vec4<f32>, 4> = P.v4_arr;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  v2_a : vec2<f32>,
-  @align(16)
-  v3_a : __packed_vec3<f32>,
-  v4_a : vec4<f32>,
-  v2_b : vec2<f32>,
-  @align(16)
-  v3_b : __packed_vec3<f32>,
-  v4_b : vec4<f32>,
-  v2_arr : array<vec2<f32>, 4>,
-  @align(16)
-  v3_arr : array<tint_packed_vec3_f32_array_element, 4u>,
-  v4_arr : array<vec4<f32>, 4>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-struct S {
-  v2_a : vec2<f32>,
-  v3_a : vec3<f32>,
-  v4_a : vec4<f32>,
-  v2_b : vec2<f32>,
-  v3_b : vec3<f32>,
-  v4_b : vec4<f32>,
-  v2_arr : array<vec2<f32>, 4>,
-  v3_arr : array<vec3<f32>, 4>,
-  v4_arr : array<vec4<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  let v2_a = P.v2_a;
-  let v3_a = vec3<f32>(P.v3_a);
-  let v4_a = P.v4_a;
-  let v2_b = P.v2_b;
-  let v3_b = vec3<f32>(P.v3_b);
-  let v4_b = P.v4_b;
-  let v2_arr : array<vec2<f32>, 4> = P.v2_arr;
-  let v3_arr : array<vec3<f32>, 4> = tint_unpack_vec3_in_composite(P.v3_arr);
-  let v4_arr : array<vec4<f32>, 4> = P.v4_arr;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3Pointers) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-@group(0) @binding(1) var<storage, read_write> arr_v : array<vec3<f32>, 4>;
-@group(0) @binding(2) var<storage, read_write> m : mat3x3<f32>;
-@group(0) @binding(3) var<storage, read_write> arr_m : array<mat3x3<f32>, 4>;
-@group(0) @binding(4) var<storage, read_write> str : S;
-
-fn f() {
-  let p_v = &v;
-  let v = *p_v;
-  *p_v = v;
-
-  let p_arr_v = &arr_v[0];
-  let arr_v = *p_arr_v;
-  *p_arr_v = arr_v;
-
-  let p_m = &m[0];
-  let m = *p_m;
-  *p_m = m;
-
-  let p_arr_m = &arr_m[0][1];
-  let arr_m = *p_arr_m;
-  *p_arr_m = arr_m;
-
-  let p_str_v = &str.v;
-  let str_v = *p_str_v;
-  *p_str_v = str_v;
-
-  let p_str_arr_v = &str.arr_v[0];
-  let str_arr_v = *p_str_arr_v;
-  *p_str_arr_v = str_arr_v;
-
-  let p_str_m = &str.m[0];
-  let str_m = *p_str_m;
-  *p_str_m = str_m;
-
-  let p_str_arr_m = &str.arr_m[0][1];
-  let str_arr_m = *p_str_arr_m;
-  *p_str_arr_m = str_arr_m;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-@group(0) @binding(1) var<storage, read_write> arr_v : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(2) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(3) var<storage, read_write> arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(4) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn f() {
-  let p_v = &(v);
-  let v = vec3<f32>(*(p_v));
-  *(p_v) = __packed_vec3<f32>(v);
-  let p_arr_v = &(arr_v[0].elements);
-  let arr_v = vec3<f32>(*(p_arr_v));
-  *(p_arr_v) = __packed_vec3<f32>(arr_v);
-  let p_m = &(m[0].elements);
-  let m = vec3<f32>(*(p_m));
-  *(p_m) = __packed_vec3<f32>(m);
-  let p_arr_m = &(arr_m[0][1].elements);
-  let arr_m = vec3<f32>(*(p_arr_m));
-  *(p_arr_m) = __packed_vec3<f32>(arr_m);
-  let p_str_v = &(str.v);
-  let str_v = vec3<f32>(*(p_str_v));
-  *(p_str_v) = __packed_vec3<f32>(str_v);
-  let p_str_arr_v = &(str.arr_v[0].elements);
-  let str_arr_v = vec3<f32>(*(p_str_arr_v));
-  *(p_str_arr_v) = __packed_vec3<f32>(str_arr_v);
-  let p_str_m = &(str.m[0].elements);
-  let str_m = vec3<f32>(*(p_str_m));
-  *(p_str_m) = __packed_vec3<f32>(str_m);
-  let p_str_arr_m = &(str.arr_m[0][1].elements);
-  let str_arr_m = vec3<f32>(*(p_str_arr_m));
-  *(p_str_arr_m) = __packed_vec3<f32>(str_arr_m);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MatrixPointers) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
-@group(0) @binding(1) var<storage, read_write> arr_m : array<mat3x3<f32>, 4>;
-@group(0) @binding(2) var<storage, read_write> str : S;
-
-fn f() {
-  let p_m = &m;
-  let m = *p_m;
-  *p_m = m;
-
-  let p_arr_m = &arr_m[0];
-  let arr_m = *p_arr_m;
-  *p_arr_m = arr_m;
-
-  let p_str_m = &str.m;
-  let str_m = *p_str_m;
-  *p_str_m = str_m;
-
-  let p_str_arr_m = &str.arr_m[0];
-  let str_arr_m = *p_str_arr_m;
-  *p_str_arr_m = str_arr_m;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-struct S {
-  m : mat3x3<f32>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(1) var<storage, read_write> arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(2) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn f() {
-  let p_m = &(m);
-  let m = tint_unpack_vec3_in_composite(*(p_m));
-  *(p_m) = tint_pack_vec3_in_composite(m);
-  let p_arr_m = &(arr_m[0]);
-  let arr_m = tint_unpack_vec3_in_composite(*(p_arr_m));
-  *(p_arr_m) = tint_pack_vec3_in_composite(arr_m);
-  let p_str_m = &(str.m);
-  let str_m = tint_unpack_vec3_in_composite(*(p_str_m));
-  *(p_str_m) = tint_pack_vec3_in_composite(str_m);
-  let p_str_arr_m = &(str.arr_m[0]);
-  let str_arr_m = tint_unpack_vec3_in_composite(*(p_str_arr_m));
-  *(p_str_arr_m) = tint_pack_vec3_in_composite(str_arr_m);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVec3Pointers) {
-    auto* src = R"(
-struct S {
-  arr_v : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_v : array<vec3<f32>, 4>;
-@group(0) @binding(1) var<storage, read_write> str : S;
-
-fn f() {
-  let p_arr_v = &arr_v;
-  let arr_v = *p_arr_v;
-  *p_arr_v = arr_v;
-
-  let p_str_arr_v = &str.arr_v;
-  let str_arr_v = *p_str_arr_v;
-  *p_str_arr_v = str_arr_v;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-struct S {
-  arr_v : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_v : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(1) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn f() {
-  let p_arr_v = &(arr_v);
-  let arr_v = tint_unpack_vec3_in_composite(*(p_arr_v));
-  *(p_arr_v) = tint_pack_vec3_in_composite(arr_v);
-  let p_str_arr_v = &(str.arr_v);
-  let str_arr_v = tint_unpack_vec3_in_composite(*(p_str_arr_v));
-  *(p_str_arr_v) = tint_pack_vec3_in_composite(str_arr_v);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrixPointers) {
-    auto* src = R"(
-struct S {
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_m : array<mat3x3<f32>, 4>;
-@group(0) @binding(1) var<storage, read_write> str : S;
-
-fn f() {
-  let p_arr_m = &arr_m;
-  let arr_m = *p_arr_m;
-  *p_arr_m = arr_m;
-
-  let p_str_arr_m = &str.arr_m;
-  let str_arr_m = *p_str_arr_m;
-  *p_str_arr_m = str_arr_m;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<mat3x3<f32>, 4u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 4u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]), tint_pack_vec3_in_composite(in[2]), tint_pack_vec3_in_composite(in[3]));
-  return result;
-}
-
-struct S {
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(1) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn f() {
-  let p_arr_m = &(arr_m);
-  let arr_m = tint_unpack_vec3_in_composite_1(*(p_arr_m));
-  *(p_arr_m) = tint_pack_vec3_in_composite_1(arr_m);
-  let p_str_arr_m = &(str.arr_m);
-  let str_arr_m = tint_unpack_vec3_in_composite_1(*(p_str_arr_m));
-  *(p_str_arr_m) = tint_pack_vec3_in_composite_1(str_arr_m);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructPointers) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> str : S;
-
-fn f() {
-  let p_str = &str;
-  let str = *p_str;
-  *p_str = str;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.v = vec3<f32>(in.v);
-  result.m = tint_unpack_vec3_in_composite(in.m);
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_2(in.arr_m);
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_2(in : array<mat3x3<f32>, 4u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 4u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]), tint_pack_vec3_in_composite(in[2]), tint_pack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_3(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.v = __packed_vec3<f32>(in.v);
-  result.m = tint_pack_vec3_in_composite(in.m);
-  result.arr_v = tint_pack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_pack_vec3_in_composite_2(in.arr_m);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn f() {
-  let p_str = &(str);
-  let str = tint_unpack_vec3_in_composite_3(*(p_str));
-  *(p_str) = tint_pack_vec3_in_composite_3(str);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, VectorPointerParameters) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-@group(0) @binding(1) var<storage, read_write> arr_v : array<vec3<f32>, 4>;
-@group(0) @binding(2) var<storage, read_write> m : mat3x3<f32>;
-@group(0) @binding(3) var<storage, read_write> arr_m : array<mat3x3<f32>, 4>;
-@group(0) @binding(4) var<storage, read_write> str : S;
-
-fn load(p : ptr<storage, vec3<f32>, read_write>) -> vec3<f32> {
-  return *p;
-}
-
-fn store(p : ptr<storage, vec3<f32>, read_write>) {
-  *p = vec3(1, 2, 3);
-}
-
-fn f() {
-  load(&v);
-  store(&v);
-  load(&arr_v[0]);
-  store(&arr_v[0]);
-  load(&m[0]);
-  store(&m[0]);
-  load(&arr_m[0][1]);
-  store(&arr_m[0][1]);
-  load(&str.v);
-  store(&str.v);
-  load(&str.arr_v[0]);
-  store(&str.arr_v[0]);
-  load(&str.m[0]);
-  store(&str.m[0]);
-  load(&str.arr_m[0][1]);
-  store(&str.arr_m[0][1]);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-@group(0) @binding(1) var<storage, read_write> arr_v : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(2) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(3) var<storage, read_write> arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(4) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn load(p : ptr<storage, __packed_vec3<f32>, read_write>) -> vec3<f32> {
-  return vec3<f32>(*(p));
-}
-
-fn store(p : ptr<storage, __packed_vec3<f32>, read_write>) {
-  *(p) = __packed_vec3<f32>(vec3(1, 2, 3));
-}
-
-fn f() {
-  load(&(v));
-  store(&(v));
-  load(&(arr_v[0].elements));
-  store(&(arr_v[0].elements));
-  load(&(m[0].elements));
-  store(&(m[0].elements));
-  load(&(arr_m[0][1].elements));
-  store(&(arr_m[0][1].elements));
-  load(&(str.v));
-  store(&(str.v));
-  load(&(str.arr_v[0].elements));
-  store(&(str.arr_v[0].elements));
-  load(&(str.m[0].elements));
-  store(&(str.m[0].elements));
-  load(&(str.arr_m[0][1].elements));
-  store(&(str.arr_m[0][1].elements));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MatrixPointerParameters) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : mat3x3<f32>;
-@group(0) @binding(1) var<storage, read_write> arr_m : array<mat3x3<f32>, 4>;
-@group(0) @binding(2) var<storage, read_write> str : S;
-
-fn load(p : ptr<storage, mat3x3<f32>, read_write>) -> mat3x3<f32> {
-  return *p;
-}
-
-fn store(p : ptr<storage, mat3x3<f32>, read_write>) {
-  *p = mat3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
-}
-
-fn f() {
-  load(&m);
-  store(&m);
-  load(&arr_m[0]);
-  store(&arr_m[0]);
-  load(&str.m);
-  store(&str.m);
-  load(&str.arr_m[0]);
-  store(&str.arr_m[0]);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-struct S {
-  m : mat3x3<f32>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(1) var<storage, read_write> arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(2) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn load(p : ptr<storage, array<tint_packed_vec3_f32_array_element, 3u>, read_write>) -> mat3x3<f32> {
-  return tint_unpack_vec3_in_composite(*(p));
-}
-
-fn store(p : ptr<storage, array<tint_packed_vec3_f32_array_element, 3u>, read_write>) {
-  *(p) = tint_pack_vec3_in_composite(mat3x3(1, 2, 3, 4, 5, 6, 7, 8, 9));
-}
-
-fn f() {
-  load(&(m));
-  store(&(m));
-  load(&(arr_m[0]));
-  store(&(arr_m[0]));
-  load(&(str.m));
-  store(&(str.m));
-  load(&(str.arr_m[0]));
-  store(&(str.arr_m[0]));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfVectorPointerParameters) {
-    auto* src = R"(
-struct S {
-  arr_v : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_v : array<vec3<f32>, 4>;
-@group(0) @binding(1) var<storage, read_write> str : S;
-
-fn load(p : ptr<storage, array<vec3<f32>, 4>, read_write>) -> array<vec3<f32>, 4> {
-  return *p;
-}
-
-fn store(p : ptr<storage, array<vec3<f32>, 4>, read_write>) {
-  *p = array(vec3(1.0), vec3(2.0), vec3(3.0), vec3(4.0));
-}
-
-fn f() {
-  load(&arr_v);
-  store(&arr_v);
-  load(&str.arr_v);
-  store(&str.arr_v);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-struct S {
-  arr_v : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_v : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(1) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn load(p : ptr<storage, array<tint_packed_vec3_f32_array_element, 4u>, read_write>) -> array<vec3<f32>, 4> {
-  return tint_unpack_vec3_in_composite(*(p));
-}
-
-fn store(p : ptr<storage, array<tint_packed_vec3_f32_array_element, 4u>, read_write>) {
-  *(p) = tint_pack_vec3_in_composite(array(vec3(1.0), vec3(2.0), vec3(3.0), vec3(4.0)));
-}
-
-fn f() {
-  load(&(arr_v));
-  store(&(arr_v));
-  load(&(str.arr_v));
-  store(&(str.arr_v));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ArrayOfMatrixPointerParameters) {
-    auto* src = R"(
-struct S {
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_m : array<mat3x3<f32>, 4>;
-@group(0) @binding(1) var<storage, read_write> str : S;
-
-fn load(p : ptr<storage, array<mat3x3<f32>, 4>, read_write>) -> array<mat3x3<f32>, 4> {
-  return *p;
-}
-
-fn store(p : ptr<storage, array<mat3x3<f32>, 4>, read_write>) {
-  *p = array(mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>());
-}
-
-fn f() {
-  load(&arr_m);
-  store(&arr_m);
-  load(&str.arr_m);
-  store(&str.arr_m);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<mat3x3<f32>, 4u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 4u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]), tint_pack_vec3_in_composite(in[2]), tint_pack_vec3_in_composite(in[3]));
-  return result;
-}
-
-struct S {
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(1) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn load(p : ptr<storage, array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>, read_write>) -> array<mat3x3<f32>, 4> {
-  return tint_unpack_vec3_in_composite_1(*(p));
-}
-
-fn store(p : ptr<storage, array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>, read_write>) {
-  *(p) = tint_pack_vec3_in_composite_1(array(mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>()));
-}
-
-fn f() {
-  load(&(arr_m));
-  store(&(arr_m));
-  load(&(str.arr_m));
-  store(&(str.arr_m));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, StructPointerParameters) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> str : S;
-
-fn load(p : ptr<storage, S, read_write>) -> S {
-  return *p;
-}
-
-fn store(p : ptr<storage, S, read_write>) {
-  *p = S();
-}
-
-fn f() {
-  load(&str);
-  store(&str);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.v = vec3<f32>(in.v);
-  result.m = tint_unpack_vec3_in_composite(in.m);
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_2(in.arr_m);
-  return result;
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_2(in : array<mat3x3<f32>, 4u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 4u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]), tint_pack_vec3_in_composite(in[2]), tint_pack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_3(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.v = __packed_vec3<f32>(in.v);
-  result.m = tint_pack_vec3_in_composite(in.m);
-  result.arr_v = tint_pack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_pack_vec3_in_composite_2(in.arr_m);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> str : S_tint_packed_vec3;
-
-fn load(p : ptr<storage, S_tint_packed_vec3, read_write>) -> S {
-  return tint_unpack_vec3_in_composite_3(*(p));
-}
-
-fn store(p : ptr<storage, S_tint_packed_vec3, read_write>) {
-  *(p) = tint_pack_vec3_in_composite_3(S());
-}
-
-fn f() {
-  load(&(str));
-  store(&(str));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_Struct) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn f() {
-  var f : S;
-  let v = f.v;
-  let arr = f.arr;
-  P = f;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.v = __packed_vec3<f32>(in.v);
-  result.arr = tint_pack_vec3_in_composite(in.arr);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn f() {
-  var f : S;
-  let v = f.v;
-  let arr = f.arr;
-  P = tint_pack_vec3_in_composite_1(f);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_NestedStruct) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-struct Outer {
-  inner : S,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : Outer;
-
-fn f() {
-  var f : Outer;
-  let v = f.inner.v;
-  let arr = f.inner.arr;
-  P = f;
-  P.inner = f.inner;
-  P.inner.v = f.inner.v;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-struct Outer_tint_packed_vec3 {
-  @align(16)
-  inner : S_tint_packed_vec3,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.v = __packed_vec3<f32>(in.v);
-  result.arr = tint_pack_vec3_in_composite(in.arr);
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_2(in : Outer) -> Outer_tint_packed_vec3 {
-  var result : Outer_tint_packed_vec3;
-  result.inner = tint_pack_vec3_in_composite_1(in.inner);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-struct Outer {
-  inner : S,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : Outer_tint_packed_vec3;
-
-fn f() {
-  var f : Outer;
-  let v = f.inner.v;
-  let arr = f.inner.arr;
-  P = tint_pack_vec3_in_composite_2(f);
-  P.inner = tint_pack_vec3_in_composite_1(f.inner);
-  P.inner.v = __packed_vec3<f32>(f.inner.v);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_AnotherStructNotShared) {
-    // Test that we can pass a pointers to a members of both shared and non-shared structs to the
-    // same function.
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-struct NotShared {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S;
-
-fn g(p : ptr<function, vec3<f32>>) -> vec3<f32> {
-  return *p;
-}
-
-fn f() {
-  var f1 : S;
-  var f2 : NotShared;
-  g(&f1.v);
-  g(&f1.arr[0]);
-  g(&f2.v);
-  g(&f2.arr[0]);
-  P = f1;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.v = __packed_vec3<f32>(in.v);
-  result.arr = tint_pack_vec3_in_composite(in.arr);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-struct NotShared {
-  v : vec3<f32>,
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> P : S_tint_packed_vec3;
-
-fn g(p : ptr<function, vec3<f32>>) -> vec3<f32> {
-  return *(p);
-}
-
-fn f() {
-  var f1 : S;
-  var f2 : NotShared;
-  g(&(f1.v));
-  g(&(f1.arr[0]));
-  g(&(f2.v));
-  g(&(f2.arr[0]));
-  P = tint_pack_vec3_in_composite_1(f1);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_InitFromLoad_ExplicitVarType) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  var f1 : S = P;
-  var f2 : vec3<f32> = P.v;
-  var f3 : mat3x3<f32> = P.m;
-  var f4 : array<vec3<f32>, 4> = P.arr_v;
-  var f5 : array<mat3x3<f32>, 4> = P.arr_m;
-  let v_1 = f1.v;
-  let v_2 = f2;
-  let v_3 = f3[0];
-  let v_4 = f4[1];
-  let v_5 = f5[2][2];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.v = vec3<f32>(in.v);
-  result.m = tint_unpack_vec3_in_composite(in.m);
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_2(in.arr_m);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  var f1 : S = tint_unpack_vec3_in_composite_3(P);
-  var f2 : vec3<f32> = vec3<f32>(P.v);
-  var f3 : mat3x3<f32> = tint_unpack_vec3_in_composite(P.m);
-  var f4 : array<vec3<f32>, 4> = tint_unpack_vec3_in_composite_1(P.arr_v);
-  var f5 : array<mat3x3<f32>, 4> = tint_unpack_vec3_in_composite_2(P.arr_m);
-  let v_1 = f1.v;
-  let v_2 = f2;
-  let v_3 = f3[0];
-  let v_4 = f4[1];
-  let v_5 = f5[2][2];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_InitFromLoad_InferredVarType) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  var f1 = P;
-  var f2 = P.v;
-  var f3 = P.m;
-  var f4 = P.arr_v;
-  var f5 = P.arr_m;
-  let v_1 = f1.v;
-  let v_2 = f2;
-  let v_3 = f3[0];
-  let v_4 = f4[1];
-  let v_5 = f5[2][2];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.v = vec3<f32>(in.v);
-  result.m = tint_unpack_vec3_in_composite(in.m);
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_2(in.arr_m);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  var f1 = tint_unpack_vec3_in_composite_3(P);
-  var f2 = vec3<f32>(P.v);
-  var f3 = tint_unpack_vec3_in_composite(P.m);
-  var f4 = tint_unpack_vec3_in_composite_1(P.arr_v);
-  var f5 = tint_unpack_vec3_in_composite_2(P.arr_m);
-  let v_1 = f1.v;
-  let v_2 = f2;
-  let v_3 = f3[0];
-  let v_4 = f4[1];
-  let v_5 = f5[2][2];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_InitFromValue_ExplicitVarType) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  var f1 : S = S();
-  var f2 : vec3<f32> = vec3<f32>();
-  var f3 : mat3x3<f32> = mat3x3<f32>();
-  var f4 : array<vec3<f32>, 4> = array(vec3<f32>(), vec3<f32>(), vec3<f32>(), vec3<f32>());
-  var f5 : array<mat3x3<f32>, 4> = array(mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>());
-  let v_1 = f1.v;
-  let v_2 = f2;
-  let v_3 = f3[0];
-  let v_4 = f4[1];
-  let v_5 = f5[2][2];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  var f1 : S = S();
-  var f2 : vec3<f32> = vec3<f32>();
-  var f3 : mat3x3<f32> = mat3x3<f32>();
-  var f4 : array<vec3<f32>, 4> = array(vec3<f32>(), vec3<f32>(), vec3<f32>(), vec3<f32>());
-  var f5 : array<mat3x3<f32>, 4> = array(mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>());
-  let v_1 = f1.v;
-  let v_2 = f2;
-  let v_3 = f3[0];
-  let v_4 = f4[1];
-  let v_5 = f5[2][2];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_InitFromValue_InferredVarType) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  var f1 = S();
-  var f2 = vec3<f32>();
-  var f3 = mat3x3<f32>();
-  var f4 = array(vec3<f32>(), vec3<f32>(), vec3<f32>(), vec3<f32>());
-  var f5 = array(mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>());
-  let v_1 = f1.v;
-  let v_2 = f2;
-  let v_3 = f3[0];
-  let v_4 = f4[1];
-  let v_5 = f5[2][2];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  var f1 = S();
-  var f2 = vec3<f32>();
-  var f3 = mat3x3<f32>();
-  var f4 = array(vec3<f32>(), vec3<f32>(), vec3<f32>(), vec3<f32>());
-  var f5 = array(mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>(), mat3x3<f32>());
-  let v_1 = f1.v;
-  let v_2 = f2;
-  let v_3 = f3[0];
-  let v_4 = f4[1];
-  let v_5 = f5[2][2];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_Pointers_Function) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn f() {
-  var f1 : S = P;
-  var f2 : vec3<f32> = P.v;
-  var f3 : array<vec3<f32>, 4>;
-  var f4 : mat3x3<f32> = P.m;
-  let pv_1 : ptr<function, vec3<f32>> = &f1.v;
-  let pv_2 : ptr<function, vec3<f32>> = &f2;
-  let pv_3 : ptr<function, vec3<f32>> = &f3[0];
-  let pv_4 : ptr<function, mat3x3<f32>> = &f1.m;
-  let pv_5 : ptr<function, mat3x3<f32>> = &f4;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.v = vec3<f32>(in.v);
-  result.m = tint_unpack_vec3_in_composite(in.m);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn f() {
-  var f1 : S = tint_unpack_vec3_in_composite_1(P);
-  var f2 : vec3<f32> = vec3<f32>(P.v);
-  var f3 : array<vec3<f32>, 4>;
-  var f4 : mat3x3<f32> = tint_unpack_vec3_in_composite(P.m);
-  let pv_1 : ptr<function, vec3<f32>> = &(f1.v);
-  let pv_2 : ptr<function, vec3<f32>> = &(f2);
-  let pv_3 : ptr<function, vec3<f32>> = &(f3[0]);
-  let pv_4 : ptr<function, mat3x3<f32>> = &(f1.m);
-  let pv_5 : ptr<function, mat3x3<f32>> = &(f4);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_Pointers_Private) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-var<private> p1 : S;
-var<private> p2 : vec3<f32>;
-var<private> p3 : array<vec3<f32>, 4>;
-var<private> p4 : mat3x3<f32>;
-
-fn f() {
-  let pv_1 : ptr<private, vec3<f32>> = &p1.v;
-  let pv_2 : ptr<private, vec3<f32>> = &p2;
-  let pv_3 : ptr<private, vec3<f32>> = &p3[0];
-  let pv_4 : ptr<private, mat3x3<f32>> = &p1.m;
-  let pv_5 : ptr<private, mat3x3<f32>> = &p4;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-var<private> p1 : S;
-
-var<private> p2 : vec3<f32>;
-
-var<private> p3 : array<vec3<f32>, 4>;
-
-var<private> p4 : mat3x3<f32>;
-
-fn f() {
-  let pv_1 : ptr<private, vec3<f32>> = &(p1.v);
-  let pv_2 : ptr<private, vec3<f32>> = &(p2);
-  let pv_3 : ptr<private, vec3<f32>> = &(p3[0]);
-  let pv_4 : ptr<private, mat3x3<f32>> = &(p1.m);
-  let pv_5 : ptr<private, mat3x3<f32>> = &(p4);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_Pointers_Workgroup) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-var<workgroup> w1 : S;
-var<workgroup> w2 : vec3<f32>;
-var<workgroup> w3 : array<vec3<f32>, 4>;
-var<workgroup> w4 : mat3x3<f32>;
-
-fn f() {
-  let pv_1 : ptr<workgroup, vec3<f32>> = &w1.v;
-  let pv_2 : ptr<workgroup, vec3<f32>> = &w2;
-  let pv_3 : ptr<workgroup, vec3<f32>> = &w3[0];
-  let pv_4 : ptr<workgroup, mat3x3<f32>> = &w1.m;
-  let pv_5 : ptr<workgroup, mat3x3<f32>> = &w4;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-var<workgroup> w1 : S_tint_packed_vec3;
-
-var<workgroup> w2 : __packed_vec3<f32>;
-
-var<workgroup> w3 : array<tint_packed_vec3_f32_array_element, 4u>;
-
-var<workgroup> w4 : array<tint_packed_vec3_f32_array_element, 3u>;
-
-fn f() {
-  let pv_1 : ptr<workgroup, __packed_vec3<f32>> = &(w1.v);
-  let pv_2 : ptr<workgroup, __packed_vec3<f32>> = &(w2);
-  let pv_3 : ptr<workgroup, __packed_vec3<f32>> = &(w3[0].elements);
-  let pv_4 : ptr<workgroup, array<tint_packed_vec3_f32_array_element, 3u>> = &(w1.m);
-  let pv_5 : ptr<workgroup, array<tint_packed_vec3_f32_array_element, 3u>> = &(w4);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MixedAddressSpace_PointerParameters) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S;
-
-fn g_v(p : ptr<function, vec3<f32>>) -> vec3<f32> {
-  return *p;
-}
-
-fn g_m(p : ptr<function, mat3x3<f32>>) -> mat3x3<f32> {
-  return *p;
-}
-
-fn f() {
-  var f1 : S = P;
-  var f2 : vec3<f32> = P.v;
-  var f3 : array<vec3<f32>, 4>;
-  var f4 : mat3x3<f32> = P.m;
-  g_v(&f1.v);
-  g_v(&f2);
-  g_v(&f3[0]);
-  g_m(&f1.m);
-  g_m(&f4);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.v = vec3<f32>(in.v);
-  result.m = tint_unpack_vec3_in_composite(in.m);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage> P : S_tint_packed_vec3;
-
-fn g_v(p : ptr<function, vec3<f32>>) -> vec3<f32> {
-  return *(p);
-}
-
-fn g_m(p : ptr<function, mat3x3<f32>>) -> mat3x3<f32> {
-  return *(p);
-}
-
-fn f() {
-  var f1 : S = tint_unpack_vec3_in_composite_1(P);
-  var f2 : vec3<f32> = vec3<f32>(P.v);
-  var f3 : array<vec3<f32>, 4>;
-  var f4 : mat3x3<f32> = tint_unpack_vec3_in_composite(P.m);
-  g_v(&(f1.v));
-  g_v(&(f2));
-  g_v(&(f3[0]));
-  g_m(&(f1.m));
-  g_m(&(f4));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, WriteVec3Swizzle_FromRef) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-
-fn f() {
-  v = v.zyx;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-fn f() {
-  v = __packed_vec3<f32>(vec3<f32>(v).zyx);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, WriteVec3Swizzle_FromValue) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> v : vec3<f32>;
-
-fn f() {
-  v = vec3f(1, 2, 3).zyx;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> v : __packed_vec3<f32>;
-
-fn f() {
-  v = __packed_vec3<f32>(vec3f(1, 2, 3).zyx);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, WriteVec3Component_FromPackedValueIndexAccessor) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-
-fn g() -> S {
-  return S();
-}
-
-fn f() {
-  s.v[0] = g().v[1];
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S_tint_packed_vec3;
-
-fn g() -> S {
-  return S();
-}
-
-fn f() {
-  s.v[0] = g().v[1];
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ExtractVec3FromStructValueExpression) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S;
-
-fn f() {
-  var v_var : vec3<f32> = S().v;
-  let v_let : vec3<f32> = S().v;
-  v_var = S().v;
-  v_var = S().v * 2.0;
-  buffer = S(S().v);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-}
-
-fn tint_pack_vec3_in_composite(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.v = __packed_vec3<f32>(in.v);
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S_tint_packed_vec3;
-
-fn f() {
-  var v_var : vec3<f32> = S().v;
-  let v_let : vec3<f32> = S().v;
-  v_var = S().v;
-  v_var = (S().v * 2.0);
-  buffer = tint_pack_vec3_in_composite(S(S().v));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ExtractArrayOfVec3FromStructValueExpression) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S;
-
-fn f() {
-  var arr_var : array<vec3<f32>, 4> = S().arr;
-  let arr_let : array<vec3<f32>, 4> = S().arr;
-  arr_var = S().arr;
-  arr_var[0] = S().arr[0] * 2.0;
-  buffer = S(S().arr);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element, 4u>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.arr = tint_pack_vec3_in_composite(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<vec3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S_tint_packed_vec3;
-
-fn f() {
-  var arr_var : array<vec3<f32>, 4> = S().arr;
-  let arr_let : array<vec3<f32>, 4> = S().arr;
-  arr_var = S().arr;
-  arr_var[0] = (S().arr[0] * 2.0);
-  buffer = tint_pack_vec3_in_composite_1(S(S().arr));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ExtractNestedArrayFromStructValueExpression) {
-    auto* src = R"(
-struct S {
-  arr : array<array<vec3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S;
-
-fn f() {
-  var arr_var : array<array<vec3<f32>, 4>, 4> = S().arr;
-  var inner_var : array<vec3<f32>, 4> = S().arr[0];
-  let arr_let : array<array<vec3<f32>, 4>, 4> = S().arr;
-  arr_var = S().arr;
-  inner_var = S().arr[0];
-  arr_var[0][0] = S().arr[0][0] * 2.0;
-  buffer = S(S().arr);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>,
-}
-
-fn tint_pack_vec3_in_composite(in : array<vec3<f32>, 4u>) -> array<tint_packed_vec3_f32_array_element, 4u> {
-  var result = array<tint_packed_vec3_f32_array_element, 4u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[3])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<array<vec3<f32>, 4u>, 4u>) -> array<array<tint_packed_vec3_f32_array_element, 4u>, 4u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]), tint_pack_vec3_in_composite(in[2]), tint_pack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_2(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.arr = tint_pack_vec3_in_composite_1(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<array<vec3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S_tint_packed_vec3;
-
-fn f() {
-  var arr_var : array<array<vec3<f32>, 4>, 4> = S().arr;
-  var inner_var : array<vec3<f32>, 4> = S().arr[0];
-  let arr_let : array<array<vec3<f32>, 4>, 4> = S().arr;
-  arr_var = S().arr;
-  inner_var = S().arr[0];
-  arr_var[0][0] = (S().arr[0][0] * 2.0);
-  buffer = tint_pack_vec3_in_composite_2(S(S().arr));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ExtractMatrixFromStructValueExpression) {
-    auto* src = R"(
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S;
-
-fn f() {
-  var m_var : mat3x3<f32> = S().m;
-  let m_let : mat3x3<f32> = S().m;
-  m_var = S().m;
-  m_var = S().m * 2.0;
-  buffer = S(S().m);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.m = tint_pack_vec3_in_composite(in.m);
-  return result;
-}
-
-struct S {
-  m : mat3x3<f32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S_tint_packed_vec3;
-
-fn f() {
-  var m_var : mat3x3<f32> = S().m;
-  let m_let : mat3x3<f32> = S().m;
-  m_var = S().m;
-  m_var = (S().m * 2.0);
-  buffer = tint_pack_vec3_in_composite_1(S(S().m));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, ExtractArrayOfMatrixFromStructValueExpression) {
-    auto* src = R"(
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S;
-
-fn f() {
-  var arr_var : array<mat3x3<f32>, 4> = S().arr;
-  let arr_let : array<mat3x3<f32>, 4> = S().arr;
-  arr_var = S().arr;
-  arr_var[0] = S().arr[0] * 2.0;
-  buffer = S(S().arr);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-}
-
-fn tint_pack_vec3_in_composite(in : mat3x3<f32>) -> array<tint_packed_vec3_f32_array_element, 3u> {
-  var result = array<tint_packed_vec3_f32_array_element, 3u>(tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[0])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[1])), tint_packed_vec3_f32_array_element(__packed_vec3<f32>(in[2])));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_1(in : array<mat3x3<f32>, 4u>) -> array<array<tint_packed_vec3_f32_array_element, 3u>, 4u> {
-  var result = array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>(tint_pack_vec3_in_composite(in[0]), tint_pack_vec3_in_composite(in[1]), tint_pack_vec3_in_composite(in[2]), tint_pack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_pack_vec3_in_composite_2(in : S) -> S_tint_packed_vec3 {
-  var result : S_tint_packed_vec3;
-  result.arr = tint_pack_vec3_in_composite_1(in.arr);
-  return result;
-}
-
-struct S {
-  arr : array<mat3x3<f32>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S_tint_packed_vec3;
-
-fn f() {
-  var arr_var : array<mat3x3<f32>, 4> = S().arr;
-  let arr_let : array<mat3x3<f32>, 4> = S().arr;
-  arr_var = S().arr;
-  arr_var[0] = (S().arr[0] * 2.0);
-  buffer = tint_pack_vec3_in_composite_2(S(S().arr));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, NestedArrays_Let) {
-    auto* src = R"(
-struct S {
-  arr_v : array<array<vec3<f32>, 4>, 4>,
-  arr_m : array<array<mat3x3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S, 4>;
-
-fn f() {
-  let full_let : array<S, 4> = arr_s;
-  let struct_let : S = arr_s[0];
-  let outer_arr_v_let : array<array<vec3<f32>, 4>, 4> = arr_s[0].arr_v;
-  let inner_arr_v_let : array<vec3<f32>, 4> = arr_s[0].arr_v[1];
-  let v_let : vec3<f32> = arr_s[0].arr_v[1][2];
-  let v_element_let : f32 = arr_s[0].arr_v[1][2].y;
-  let outer_arr_m_let : array<array<mat3x3<f32>, 4>, 4> = arr_s[0].arr_m;
-  let inner_arr_m_let : array<mat3x3<f32>, 4> = arr_s[0].arr_m[1];
-  let m_let : mat3x3<f32> = arr_s[0].arr_m[1][2];
-  let m_col_let : vec3<f32> = arr_s[0].arr_m[1][2][0];
-  let m_element_let : f32 = arr_s[0].arr_m[1][2][0].y;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_v : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>,
-  @align(16)
-  arr_m : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>) -> array<array<vec3<f32>, 4u>, 4u> {
-  var result = array<array<vec3<f32>, 4u>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite_2(in[0]), tint_unpack_vec3_in_composite_2(in[1]), tint_unpack_vec3_in_composite_2(in[2]), tint_unpack_vec3_in_composite_2(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_4(in : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>, 4u>) -> array<array<mat3x3<f32>, 4u>, 4u> {
-  var result = array<array<mat3x3<f32>, 4u>, 4u>(tint_unpack_vec3_in_composite_3(in[0]), tint_unpack_vec3_in_composite_3(in[1]), tint_unpack_vec3_in_composite_3(in[2]), tint_unpack_vec3_in_composite_3(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_5(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_4(in.arr_m);
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_6(in : array<S_tint_packed_vec3, 4u>) -> array<S, 4u> {
-  var result = array<S, 4u>(tint_unpack_vec3_in_composite_5(in[0]), tint_unpack_vec3_in_composite_5(in[1]), tint_unpack_vec3_in_composite_5(in[2]), tint_unpack_vec3_in_composite_5(in[3]));
-  return result;
-}
-
-struct S {
-  arr_v : array<array<vec3<f32>, 4>, 4>,
-  arr_m : array<array<mat3x3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S_tint_packed_vec3, 4u>;
-
-fn f() {
-  let full_let : array<S, 4> = tint_unpack_vec3_in_composite_6(arr_s);
-  let struct_let : S = tint_unpack_vec3_in_composite_5(arr_s[0]);
-  let outer_arr_v_let : array<array<vec3<f32>, 4>, 4> = tint_unpack_vec3_in_composite_1(arr_s[0].arr_v);
-  let inner_arr_v_let : array<vec3<f32>, 4> = tint_unpack_vec3_in_composite(arr_s[0].arr_v[1]);
-  let v_let : vec3<f32> = vec3<f32>(arr_s[0].arr_v[1][2].elements);
-  let v_element_let : f32 = arr_s[0].arr_v[1][2].elements.y;
-  let outer_arr_m_let : array<array<mat3x3<f32>, 4>, 4> = tint_unpack_vec3_in_composite_4(arr_s[0].arr_m);
-  let inner_arr_m_let : array<mat3x3<f32>, 4> = tint_unpack_vec3_in_composite_3(arr_s[0].arr_m[1]);
-  let m_let : mat3x3<f32> = tint_unpack_vec3_in_composite_2(arr_s[0].arr_m[1][2]);
-  let m_col_let : vec3<f32> = vec3<f32>(arr_s[0].arr_m[1][2][0].elements);
-  let m_element_let : f32 = arr_s[0].arr_m[1][2][0].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, NestedArrays_VarInit_Small) {
-    auto* src = R"(
-struct S {
-  arr_v : array<array<vec3<f32>, 4>, 4>,
-  arr_m : array<array<mat3x3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S, 4>;
-
-fn f() {
-  var full_var : array<S, 4> = arr_s;
-  var struct_var : S = arr_s[0];
-  var outer_arr_v_var : array<array<vec3<f32>, 4>, 4> = arr_s[0].arr_v;
-  var inner_arr_v_var : array<vec3<f32>, 4> = arr_s[0].arr_v[1];
-  var v_var : vec3<f32> = arr_s[0].arr_v[1][2];
-  var v_element_var : f32 = arr_s[0].arr_v[1][2].y;
-  var outer_arr_m_var : array<array<mat3x3<f32>, 4>, 4> = arr_s[0].arr_m;
-  var inner_arr_m_var : array<mat3x3<f32>, 4> = arr_s[0].arr_m[1];
-  var m_var : mat3x3<f32> = arr_s[0].arr_m[1][2];
-  var m_col_var : vec3<f32> = arr_s[0].arr_m[1][2][0];
-  var m_element_var : f32 = arr_s[0].arr_m[1][2][0].y;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_v : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>,
-  @align(16)
-  arr_m : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>) -> array<array<vec3<f32>, 4u>, 4u> {
-  var result = array<array<vec3<f32>, 4u>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite_2(in[0]), tint_unpack_vec3_in_composite_2(in[1]), tint_unpack_vec3_in_composite_2(in[2]), tint_unpack_vec3_in_composite_2(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_4(in : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>, 4u>) -> array<array<mat3x3<f32>, 4u>, 4u> {
-  var result = array<array<mat3x3<f32>, 4u>, 4u>(tint_unpack_vec3_in_composite_3(in[0]), tint_unpack_vec3_in_composite_3(in[1]), tint_unpack_vec3_in_composite_3(in[2]), tint_unpack_vec3_in_composite_3(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_5(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_4(in.arr_m);
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_6(in : array<S_tint_packed_vec3, 4u>) -> array<S, 4u> {
-  var result = array<S, 4u>(tint_unpack_vec3_in_composite_5(in[0]), tint_unpack_vec3_in_composite_5(in[1]), tint_unpack_vec3_in_composite_5(in[2]), tint_unpack_vec3_in_composite_5(in[3]));
-  return result;
-}
-
-struct S {
-  arr_v : array<array<vec3<f32>, 4>, 4>,
-  arr_m : array<array<mat3x3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S_tint_packed_vec3, 4u>;
-
-fn f() {
-  var full_var : array<S, 4> = tint_unpack_vec3_in_composite_6(arr_s);
-  var struct_var : S = tint_unpack_vec3_in_composite_5(arr_s[0]);
-  var outer_arr_v_var : array<array<vec3<f32>, 4>, 4> = tint_unpack_vec3_in_composite_1(arr_s[0].arr_v);
-  var inner_arr_v_var : array<vec3<f32>, 4> = tint_unpack_vec3_in_composite(arr_s[0].arr_v[1]);
-  var v_var : vec3<f32> = vec3<f32>(arr_s[0].arr_v[1][2].elements);
-  var v_element_var : f32 = arr_s[0].arr_v[1][2].elements.y;
-  var outer_arr_m_var : array<array<mat3x3<f32>, 4>, 4> = tint_unpack_vec3_in_composite_4(arr_s[0].arr_m);
-  var inner_arr_m_var : array<mat3x3<f32>, 4> = tint_unpack_vec3_in_composite_3(arr_s[0].arr_m[1]);
-  var m_var : mat3x3<f32> = tint_unpack_vec3_in_composite_2(arr_s[0].arr_m[1][2]);
-  var m_col_var : vec3<f32> = vec3<f32>(arr_s[0].arr_m[1][2][0].elements);
-  var m_element_var : f32 = arr_s[0].arr_m[1][2][0].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, NestedArrays_VarInit_Large) {
-    auto* src = R"(
-struct S {
-  arr_v : array<array<vec3<f32>, 9>, 9>,
-  arr_m : array<array<mat3x3<f32>, 9>, 9>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S, 9>;
-
-fn f() {
-  var full_var : array<S, 9> = arr_s;
-  var struct_var : S = arr_s[0];
-  var outer_arr_v_var : array<array<vec3<f32>, 9>, 9> = arr_s[0].arr_v;
-  var inner_arr_v_var : array<vec3<f32>, 9> = arr_s[0].arr_v[1];
-  var v_var : vec3<f32> = arr_s[0].arr_v[1][2];
-  var v_element_var : f32 = arr_s[0].arr_v[1][2].y;
-  var outer_arr_m_var : array<array<mat3x3<f32>, 9>, 9> = arr_s[0].arr_m;
-  var inner_arr_m_var : array<mat3x3<f32>, 9> = arr_s[0].arr_m[1];
-  var m_var : mat3x3<f32> = arr_s[0].arr_m[1][2];
-  var m_col_var : vec3<f32> = arr_s[0].arr_m[1][2][0];
-  var m_element_var : f32 = arr_s[0].arr_m[1][2][0].y;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_v : array<array<tint_packed_vec3_f32_array_element, 9u>, 9u>,
-  @align(16)
-  arr_m : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>, 9u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 9u>) -> array<vec3<f32>, 9u> {
-  var result : array<vec3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = vec3<f32>(in[i].elements);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 9u>, 9u>) -> array<array<vec3<f32>, 9u>, 9u> {
-  var result : array<array<vec3<f32>, 9u>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite(in[i]);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>) -> array<mat3x3<f32>, 9u> {
-  var result : array<mat3x3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite_2(in[i]);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_4(in : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>, 9u>) -> array<array<mat3x3<f32>, 9u>, 9u> {
-  var result : array<array<mat3x3<f32>, 9u>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite_3(in[i]);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_5(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_4(in.arr_m);
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_6(in : array<S_tint_packed_vec3, 9u>) -> array<S, 9u> {
-  var result : array<S, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite_5(in[i]);
-  }
-  return result;
-}
-
-struct S {
-  arr_v : array<array<vec3<f32>, 9>, 9>,
-  arr_m : array<array<mat3x3<f32>, 9>, 9>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S_tint_packed_vec3, 9u>;
-
-fn f() {
-  var full_var : array<S, 9> = tint_unpack_vec3_in_composite_6(arr_s);
-  var struct_var : S = tint_unpack_vec3_in_composite_5(arr_s[0]);
-  var outer_arr_v_var : array<array<vec3<f32>, 9>, 9> = tint_unpack_vec3_in_composite_1(arr_s[0].arr_v);
-  var inner_arr_v_var : array<vec3<f32>, 9> = tint_unpack_vec3_in_composite(arr_s[0].arr_v[1]);
-  var v_var : vec3<f32> = vec3<f32>(arr_s[0].arr_v[1][2].elements);
-  var v_element_var : f32 = arr_s[0].arr_v[1][2].elements.y;
-  var outer_arr_m_var : array<array<mat3x3<f32>, 9>, 9> = tint_unpack_vec3_in_composite_4(arr_s[0].arr_m);
-  var inner_arr_m_var : array<mat3x3<f32>, 9> = tint_unpack_vec3_in_composite_3(arr_s[0].arr_m[1]);
-  var m_var : mat3x3<f32> = tint_unpack_vec3_in_composite_2(arr_s[0].arr_m[1][2]);
-  var m_col_var : vec3<f32> = vec3<f32>(arr_s[0].arr_m[1][2][0].elements);
-  var m_element_var : f32 = arr_s[0].arr_m[1][2][0].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, NestedArrays_VarAssignment_Small) {
-    auto* src = R"(
-struct S {
-  arr_v : array<array<vec3<f32>, 4>, 4>,
-  arr_m : array<array<mat3x3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S, 4>;
-
-fn f() {
-  var full_var : array<S, 4>;
-  var struct_var : S;
-  var outer_arr_v_var : array<array<vec3<f32>, 4>, 4>;
-  var inner_arr_v_var : array<vec3<f32>, 4>;
-  var v_var : vec3<f32>;
-  var v_element_var : f32;
-  var outer_arr_m_var : array<array<mat3x3<f32>, 4>, 4>;
-  var inner_arr_m_var : array<mat3x3<f32>, 4>;
-  var m_var : mat3x3<f32>;
-  var m_col_var : vec3<f32>;
-  var m_element_var : f32;
-
-  full_var = arr_s;
-  struct_var = arr_s[0];
-  outer_arr_v_var = arr_s[0].arr_v;
-  inner_arr_v_var = arr_s[0].arr_v[1];
-  v_var = arr_s[0].arr_v[1][2];
-  v_element_var = arr_s[0].arr_v[1][2].y;
-  outer_arr_m_var = arr_s[0].arr_m;
-  inner_arr_m_var = arr_s[0].arr_m[1];
-  m_var = arr_s[0].arr_m[1][2];
-  m_col_var = arr_s[0].arr_m[1][2][0];
-  m_element_var = arr_s[0].arr_m[1][2][0].y;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_v : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>,
-  @align(16)
-  arr_m : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>) -> array<array<vec3<f32>, 4u>, 4u> {
-  var result = array<array<vec3<f32>, 4u>, 4u>(tint_unpack_vec3_in_composite(in[0]), tint_unpack_vec3_in_composite(in[1]), tint_unpack_vec3_in_composite(in[2]), tint_unpack_vec3_in_composite(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>) -> array<mat3x3<f32>, 4u> {
-  var result = array<mat3x3<f32>, 4u>(tint_unpack_vec3_in_composite_2(in[0]), tint_unpack_vec3_in_composite_2(in[1]), tint_unpack_vec3_in_composite_2(in[2]), tint_unpack_vec3_in_composite_2(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_4(in : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>, 4u>) -> array<array<mat3x3<f32>, 4u>, 4u> {
-  var result = array<array<mat3x3<f32>, 4u>, 4u>(tint_unpack_vec3_in_composite_3(in[0]), tint_unpack_vec3_in_composite_3(in[1]), tint_unpack_vec3_in_composite_3(in[2]), tint_unpack_vec3_in_composite_3(in[3]));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_5(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_4(in.arr_m);
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_6(in : array<S_tint_packed_vec3, 4u>) -> array<S, 4u> {
-  var result = array<S, 4u>(tint_unpack_vec3_in_composite_5(in[0]), tint_unpack_vec3_in_composite_5(in[1]), tint_unpack_vec3_in_composite_5(in[2]), tint_unpack_vec3_in_composite_5(in[3]));
-  return result;
-}
-
-struct S {
-  arr_v : array<array<vec3<f32>, 4>, 4>,
-  arr_m : array<array<mat3x3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S_tint_packed_vec3, 4u>;
-
-fn f() {
-  var full_var : array<S, 4>;
-  var struct_var : S;
-  var outer_arr_v_var : array<array<vec3<f32>, 4>, 4>;
-  var inner_arr_v_var : array<vec3<f32>, 4>;
-  var v_var : vec3<f32>;
-  var v_element_var : f32;
-  var outer_arr_m_var : array<array<mat3x3<f32>, 4>, 4>;
-  var inner_arr_m_var : array<mat3x3<f32>, 4>;
-  var m_var : mat3x3<f32>;
-  var m_col_var : vec3<f32>;
-  var m_element_var : f32;
-  full_var = tint_unpack_vec3_in_composite_6(arr_s);
-  struct_var = tint_unpack_vec3_in_composite_5(arr_s[0]);
-  outer_arr_v_var = tint_unpack_vec3_in_composite_1(arr_s[0].arr_v);
-  inner_arr_v_var = tint_unpack_vec3_in_composite(arr_s[0].arr_v[1]);
-  v_var = vec3<f32>(arr_s[0].arr_v[1][2].elements);
-  v_element_var = arr_s[0].arr_v[1][2].elements.y;
-  outer_arr_m_var = tint_unpack_vec3_in_composite_4(arr_s[0].arr_m);
-  inner_arr_m_var = tint_unpack_vec3_in_composite_3(arr_s[0].arr_m[1]);
-  m_var = tint_unpack_vec3_in_composite_2(arr_s[0].arr_m[1][2]);
-  m_col_var = vec3<f32>(arr_s[0].arr_m[1][2][0].elements);
-  m_element_var = arr_s[0].arr_m[1][2][0].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, NestedArrays_VarAssignment_Large) {
-    auto* src = R"(
-struct S {
-  arr_v : array<array<vec3<f32>, 9>, 9>,
-  arr_m : array<array<mat3x3<f32>, 9>, 9>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S, 9>;
-
-fn f() {
-  var full_var : array<S, 9>;
-  var struct_var : S;
-  var outer_arr_v_var : array<array<vec3<f32>, 9>, 9>;
-  var inner_arr_v_var : array<vec3<f32>, 9>;
-  var v_var : vec3<f32>;
-  var v_element_var : f32;
-  var outer_arr_m_var : array<array<mat3x3<f32>, 9>, 9>;
-  var inner_arr_m_var : array<mat3x3<f32>, 9>;
-  var m_var : mat3x3<f32>;
-  var m_col_var : vec3<f32>;
-  var m_element_var : f32;
-
-  full_var = arr_s;
-  struct_var = arr_s[0];
-  outer_arr_v_var = arr_s[0].arr_v;
-  inner_arr_v_var = arr_s[0].arr_v[1];
-  v_var = arr_s[0].arr_v[1][2];
-  v_element_var = arr_s[0].arr_v[1][2].y;
-  outer_arr_m_var = arr_s[0].arr_m;
-  inner_arr_m_var = arr_s[0].arr_m[1];
-  m_var = arr_s[0].arr_m[1][2];
-  m_col_var = arr_s[0].arr_m[1][2][0];
-  m_element_var = arr_s[0].arr_m[1][2][0].y;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr_v : array<array<tint_packed_vec3_f32_array_element, 9u>, 9u>,
-  @align(16)
-  arr_m : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>, 9u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 9u>) -> array<vec3<f32>, 9u> {
-  var result : array<vec3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = vec3<f32>(in[i].elements);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_1(in : array<array<tint_packed_vec3_f32_array_element, 9u>, 9u>) -> array<array<vec3<f32>, 9u>, 9u> {
-  var result : array<array<vec3<f32>, 9u>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite(in[i]);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_2(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_3(in : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>) -> array<mat3x3<f32>, 9u> {
-  var result : array<mat3x3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite_2(in[i]);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_4(in : array<array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>, 9u>) -> array<array<mat3x3<f32>, 9u>, 9u> {
-  var result : array<array<mat3x3<f32>, 9u>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite_3(in[i]);
-  }
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_5(in : S_tint_packed_vec3) -> S {
-  var result : S;
-  result.arr_v = tint_unpack_vec3_in_composite_1(in.arr_v);
-  result.arr_m = tint_unpack_vec3_in_composite_4(in.arr_m);
-  return result;
-}
-
-fn tint_unpack_vec3_in_composite_6(in : array<S_tint_packed_vec3, 9u>) -> array<S, 9u> {
-  var result : array<S, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = tint_unpack_vec3_in_composite_5(in[i]);
-  }
-  return result;
-}
-
-struct S {
-  arr_v : array<array<vec3<f32>, 9>, 9>,
-  arr_m : array<array<mat3x3<f32>, 9>, 9>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_s : array<S_tint_packed_vec3, 9u>;
-
-fn f() {
-  var full_var : array<S, 9>;
-  var struct_var : S;
-  var outer_arr_v_var : array<array<vec3<f32>, 9>, 9>;
-  var inner_arr_v_var : array<vec3<f32>, 9>;
-  var v_var : vec3<f32>;
-  var v_element_var : f32;
-  var outer_arr_m_var : array<array<mat3x3<f32>, 9>, 9>;
-  var inner_arr_m_var : array<mat3x3<f32>, 9>;
-  var m_var : mat3x3<f32>;
-  var m_col_var : vec3<f32>;
-  var m_element_var : f32;
-  full_var = tint_unpack_vec3_in_composite_6(arr_s);
-  struct_var = tint_unpack_vec3_in_composite_5(arr_s[0]);
-  outer_arr_v_var = tint_unpack_vec3_in_composite_1(arr_s[0].arr_v);
-  inner_arr_v_var = tint_unpack_vec3_in_composite(arr_s[0].arr_v[1]);
-  v_var = vec3<f32>(arr_s[0].arr_v[1][2].elements);
-  v_element_var = arr_s[0].arr_v[1][2].elements.y;
-  outer_arr_m_var = tint_unpack_vec3_in_composite_4(arr_s[0].arr_m);
-  inner_arr_m_var = tint_unpack_vec3_in_composite_3(arr_s[0].arr_m[1]);
-  m_var = tint_unpack_vec3_in_composite_2(arr_s[0].arr_m[1][2]);
-  m_col_var = vec3<f32>(arr_s[0].arr_m[1][2][0].elements);
-  m_element_var = arr_s[0].arr_m[1][2][0].elements.y;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, RuntimeSizedArray) {
-    auto* src = R"(
-struct S {
-  arr : array<vec3<f32>>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_v : array<vec3<f32>>;
-@group(0) @binding(1) var<storage, read_write> s : S;
-
-fn main() {
-  s.arr[0] = arr_v[0];
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  arr : array<tint_packed_vec3_f32_array_element>,
-}
-
-struct S {
-  arr : array<vec3<f32>>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_v : array<tint_packed_vec3_f32_array_element>;
-
-@group(0) @binding(1) var<storage, read_write> s : S_tint_packed_vec3;
-
-fn main() {
-  s.arr[0].elements = arr_v[0].elements;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Mat3x3_F16_Uniform) {
-    // Test that array element alignment validation rules do not trigger when we rewrite an f16
-    // matrix into an array of vec3s in uniform storage.
-    auto* src = R"(
-enable f16;
-@group(0) @binding(0) var<uniform> m : mat3x3<f16>;
-
-fn g(p : ptr<uniform, mat3x3<f16>>) -> vec3<f16> {
-  return (*p)[0] + vec3<f16>(1);
-}
-
-fn f() {
-  let v = g(&m);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-enable f16;
-
-struct tint_packed_vec3_f16_array_element {
-  @align(8)
-  elements : __packed_vec3<f16>,
-}
-
-@group(0) @binding(0) var<uniform> m : array<tint_packed_vec3_f16_array_element, 3u>;
-
-fn g(p : ptr<uniform, array<tint_packed_vec3_f16_array_element, 3u>>) -> vec3<f16> {
-  return (vec3<f16>((*(p))[0].elements) + vec3<f16>(1));
-}
-
-fn f() {
-  let v = g(&(m));
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MultipleComponentTypes_StructMembers) {
-    auto* src = R"(
-enable f16;
-
-struct S {
-  f : vec3f,
-  h : vec3h,
-  i : vec3i,
-  u : vec3u,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-
-fn f() {
-  let f = s.f;
-  let h = s.h;
-  let i = s.i;
-  let u = s.u;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-enable f16;
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  f : __packed_vec3<f32>,
-  @align(8)
-  h : __packed_vec3<f16>,
-  @align(16)
-  i : __packed_vec3<i32>,
-  @align(16)
-  u : __packed_vec3<u32>,
-}
-
-struct S {
-  f : vec3f,
-  h : vec3h,
-  i : vec3i,
-  u : vec3u,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S_tint_packed_vec3;
-
-fn f() {
-  let f = vec3<f32>(s.f);
-  let h = vec3<f16>(s.h);
-  let i = vec3<i32>(s.i);
-  let u = vec3<u32>(s.u);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, MultipleComponentTypes_ArrayElement) {
-    auto* src = R"(
-enable f16;
-
-@group(0) @binding(0) var<storage, read_write> arr_f : array<vec3f>;
-@group(0) @binding(1) var<storage, read_write> arr_h : array<vec3h>;
-@group(0) @binding(2) var<storage, read_write> arr_i : array<vec3i>;
-@group(0) @binding(3) var<storage, read_write> arr_u : array<vec3u>;
-
-fn main() {
-  let f = arr_f[0];
-  let h = arr_h[0];
-  let i = arr_i[0];
-  let u = arr_u[0];
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-enable f16;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct tint_packed_vec3_f16_array_element {
-  @align(8)
-  elements : __packed_vec3<f16>,
-}
-
-struct tint_packed_vec3_i32_array_element {
-  @align(16)
-  elements : __packed_vec3<i32>,
-}
-
-struct tint_packed_vec3_u32_array_element {
-  @align(16)
-  elements : __packed_vec3<u32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> arr_f : array<tint_packed_vec3_f32_array_element>;
-
-@group(0) @binding(1) var<storage, read_write> arr_h : array<tint_packed_vec3_f16_array_element>;
-
-@group(0) @binding(2) var<storage, read_write> arr_i : array<tint_packed_vec3_i32_array_element>;
-
-@group(0) @binding(3) var<storage, read_write> arr_u : array<tint_packed_vec3_u32_array_element>;
-
-fn main() {
-  let f = vec3<f32>(arr_f[0].elements);
-  let h = vec3<f16>(arr_h[0].elements);
-  let i = vec3<i32>(arr_i[0].elements);
-  let u = vec3<u32>(arr_u[0].elements);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Arithmetic_FromRef) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> buffer_v : vec3<f32>;
-@group(0) @binding(1) var<storage, read_write> buffer_m : mat3x3<f32>;
-@group(0) @binding(2) var<storage, read_write> buffer_arr_v : array<vec3<f32>, 4>;
-@group(0) @binding(3) var<storage, read_write> buffer_arr_m : array<mat3x3<f32>, 4>;
-@group(0) @binding(4) var<storage, read_write> buffer_nested_arr_v : array<array<vec3<f32>, 4>, 4>;
-
-fn f() {
-  var v : vec3<f32> = buffer_v * 2;
-  v = -v;
-  v = buffer_m * v;
-  v = buffer_m[0] + v;
-  v = buffer_arr_v[0] + v;
-  v = buffer_arr_m[0] * v;
-  v = buffer_arr_m[0][1] + v;
-  v = buffer_nested_arr_v[0][0] + v;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer_v : __packed_vec3<f32>;
-
-@group(0) @binding(1) var<storage, read_write> buffer_m : array<tint_packed_vec3_f32_array_element, 3u>;
-
-@group(0) @binding(2) var<storage, read_write> buffer_arr_v : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(3) var<storage, read_write> buffer_arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(4) var<storage, read_write> buffer_nested_arr_v : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>;
-
-fn f() {
-  var v : vec3<f32> = (vec3<f32>(buffer_v) * 2);
-  v = -(v);
-  v = (tint_unpack_vec3_in_composite(buffer_m) * v);
-  v = (vec3<f32>(buffer_m[0].elements) + v);
-  v = (vec3<f32>(buffer_arr_v[0].elements) + v);
-  v = (tint_unpack_vec3_in_composite(buffer_arr_m[0]) * v);
-  v = (vec3<f32>(buffer_arr_m[0][1].elements) + v);
-  v = (vec3<f32>(buffer_nested_arr_v[0][0].elements) + v);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Arithmetic_FromValue) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> buffer_v : vec3f;
-
-fn f() {
-  var v : vec3f = buffer_v;
-  v = -vec3f(1, 2, 3);
-  v = mat3x3f() * v;
-  v = mat3x3f()[0] + v;
-  v = array<vec3f, 4>()[0] + v;
-  v = array<mat3x3f, 4>()[0] * v;
-  v = array<mat3x3f, 4>()[0][1] + v;
-  v = array<array<vec3f, 4>, 4>()[0][0] + v;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-@group(0) @binding(0) var<storage, read_write> buffer_v : __packed_vec3<f32>;
-
-fn f() {
-  var v : vec3f = vec3<f32>(buffer_v);
-  v = -(vec3f(1, 2, 3));
-  v = (mat3x3f() * v);
-  v = (mat3x3f()[0] + v);
-  v = (array<vec3f, 4>()[0] + v);
-  v = (array<mat3x3f, 4>()[0] * v);
-  v = (array<mat3x3f, 4>()[0][1] + v);
-  v = (array<array<vec3f, 4>, 4>()[0][0] + v);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Arithmetic_FromRefStruct) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-  nested_arr_v : array<array<vec3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S;
-
-fn f() {
-  var v : vec3<f32> = buffer.v * 2;
-  v = -v;
-  v = buffer.m * v;
-  v = buffer.m[0] + v;
-  v = buffer.arr_v[0] + v;
-  v = buffer.arr_m[0] * v;
-  v = buffer.arr_m[0][1] + v;
-  v = buffer.nested_arr_v[0][0] + v;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-  @align(16)
-  nested_arr_v : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 3u>) -> mat3x3<f32> {
-  var result = mat3x3<f32>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements));
-  return result;
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-  nested_arr_v : array<array<vec3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S_tint_packed_vec3;
-
-fn f() {
-  var v : vec3<f32> = (vec3<f32>(buffer.v) * 2);
-  v = -(v);
-  v = (tint_unpack_vec3_in_composite(buffer.m) * v);
-  v = (vec3<f32>(buffer.m[0].elements) + v);
-  v = (vec3<f32>(buffer.arr_v[0].elements) + v);
-  v = (tint_unpack_vec3_in_composite(buffer.arr_m[0]) * v);
-  v = (vec3<f32>(buffer.arr_m[0][1].elements) + v);
-  v = (vec3<f32>(buffer.nested_arr_v[0][0].elements) + v);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Arithmetic_FromValueStruct) {
-    auto* src = R"(
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-  nested_arr_v : array<array<vec3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S;
-
-fn f() {
-  var v : vec3<f32> = S().v;
-  v = -S().v;
-  v = S().m * v;
-  v = S().m[0] + v;
-  v = S().arr_v[0] + v;
-  v = S().arr_m[0] * v;
-  v = S().arr_m[0][1] + v;
-  v = S().nested_arr_v[0][0] + v;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : __packed_vec3<f32>,
-  @align(16)
-  m : array<tint_packed_vec3_f32_array_element, 3u>,
-  @align(16)
-  arr_v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-  @align(16)
-  nested_arr_v : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>,
-}
-
-struct S {
-  v : vec3<f32>,
-  m : mat3x3<f32>,
-  arr_v : array<vec3<f32>, 4>,
-  arr_m : array<mat3x3<f32>, 4>,
-  nested_arr_v : array<array<vec3<f32>, 4>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : S_tint_packed_vec3;
-
-fn f() {
-  var v : vec3<f32> = S().v;
-  v = -(S().v);
-  v = (S().m * v);
-  v = (S().m[0] + v);
-  v = (S().arr_v[0] + v);
-  v = (S().arr_m[0] * v);
-  v = (S().arr_m[0][1] + v);
-  v = (S().nested_arr_v[0][0] + v);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Aliases_Small) {
-    auto* src = R"(
-alias VecArray = array<vec3<f32>, 4>;
-alias MatArray = array<mat3x3<f32>, 4>;
-alias NestedArray = array<VecArray, 4>;
-
-struct S {
-  v : VecArray,
-  m : MatArray,
-  n : NestedArray,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-@group(0) @binding(1) var<storage, read_write> arr_v : VecArray;
-@group(0) @binding(2) var<storage, read_write> arr_m : MatArray;
-@group(0) @binding(3) var<storage, read_write> arr_n : NestedArray;
-
-fn g(p : ptr<function, VecArray>) {
-}
-
-fn f() {
-  var f_arr_v : VecArray = s.v;
-  g(&f_arr_v);
-
-  arr_v = s.v;
-  arr_m[0] = s.m[0];
-  arr_n[1][2] = s.n[1][2];
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : array<tint_packed_vec3_f32_array_element, 4u>,
-  @align(16)
-  m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>,
-  @align(16)
-  n : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 4u>) -> array<vec3<f32>, 4u> {
-  var result = array<vec3<f32>, 4u>(vec3<f32>(in[0].elements), vec3<f32>(in[1].elements), vec3<f32>(in[2].elements), vec3<f32>(in[3].elements));
-  return result;
-}
-
-alias VecArray = array<vec3<f32>, 4>;
-
-alias MatArray = array<mat3x3<f32>, 4>;
-
-alias NestedArray = array<VecArray, 4>;
-
-struct S {
-  v : VecArray,
-  m : MatArray,
-  n : NestedArray,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<storage, read_write> arr_v : array<tint_packed_vec3_f32_array_element, 4u>;
-
-@group(0) @binding(2) var<storage, read_write> arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 4u>;
-
-@group(0) @binding(3) var<storage, read_write> arr_n : array<array<tint_packed_vec3_f32_array_element, 4u>, 4u>;
-
-fn g(p : ptr<function, VecArray>) {
-}
-
-fn f() {
-  var f_arr_v : VecArray = tint_unpack_vec3_in_composite(s.v);
-  g(&(f_arr_v));
-  arr_v = s.v;
-  arr_m[0] = s.m[0];
-  arr_n[1][2].elements = s.n[1][2].elements;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Aliases_Large) {
-    auto* src = R"(
-alias VecArray = array<vec3<f32>, 9>;
-alias MatArray = array<mat3x3<f32>, 9>;
-alias NestedArray = array<VecArray, 9>;
-
-struct S {
-  v : VecArray,
-  m : MatArray,
-  n : NestedArray,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-@group(0) @binding(1) var<storage, read_write> arr_v : VecArray;
-@group(0) @binding(2) var<storage, read_write> arr_m : MatArray;
-@group(0) @binding(3) var<storage, read_write> arr_n : NestedArray;
-
-fn g(p : ptr<function, VecArray>) {
-}
-
-fn f() {
-  var f_arr_v : VecArray = s.v;
-  g(&f_arr_v);
-
-  arr_v = s.v;
-  arr_m[0] = s.m[0];
-  arr_n[1][2] = s.n[1][2];
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct tint_packed_vec3_f32_array_element {
-  @align(16)
-  elements : __packed_vec3<f32>,
-}
-
-struct S_tint_packed_vec3 {
-  @align(16)
-  v : array<tint_packed_vec3_f32_array_element, 9u>,
-  @align(16)
-  m : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>,
-  @align(16)
-  n : array<array<tint_packed_vec3_f32_array_element, 9u>, 9u>,
-}
-
-fn tint_unpack_vec3_in_composite(in : array<tint_packed_vec3_f32_array_element, 9u>) -> array<vec3<f32>, 9u> {
-  var result : array<vec3<f32>, 9u>;
-  for(var i : u32; (i < 9u); i = (i + 1)) {
-    result[i] = vec3<f32>(in[i].elements);
-  }
-  return result;
-}
-
-alias VecArray = array<vec3<f32>, 9>;
-
-alias MatArray = array<mat3x3<f32>, 9>;
-
-alias NestedArray = array<VecArray, 9>;
-
-struct S {
-  v : VecArray,
-  m : MatArray,
-  n : NestedArray,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S_tint_packed_vec3;
-
-@group(0) @binding(1) var<storage, read_write> arr_v : array<tint_packed_vec3_f32_array_element, 9u>;
-
-@group(0) @binding(2) var<storage, read_write> arr_m : array<array<tint_packed_vec3_f32_array_element, 3u>, 9u>;
-
-@group(0) @binding(3) var<storage, read_write> arr_n : array<array<tint_packed_vec3_f32_array_element, 9u>, 9u>;
-
-fn g(p : ptr<function, VecArray>) {
-}
-
-fn f() {
-  var f_arr_v : VecArray = tint_unpack_vec3_in_composite(s.v);
-  g(&(f_arr_v));
-  arr_v = s.v;
-  arr_m[0] = s.m[0];
-  arr_n[1][2].elements = s.n[1][2].elements;
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PackedVec3Test, Vec3Bool) {
-    // Make sure that we don't rewrite vec3<bool> types, as the `packed_bool<n>` types are reserved
-    // in MSL and might not be supported everywhere.
-    auto* src = R"(
-struct S {
-  vf : vec3<f32>,
-  af : array<vec3<f32>, 4>,
-  vb : vec3<bool>,
-  ab : array<vec3<bool>, 4>,
-}
-
-// Create a vec3 storage buffer so that the transform is not skipped.
-@group(0) @binding(0) var<storage, read_write> buffer : vec3<f32>;
-
-fn f() {
-  var f : S;
-  f.vf = buffer;
-  buffer = f.af[0];
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_internal_relaxed_uniform_layout;
-
-struct S {
-  vf : vec3<f32>,
-  af : array<vec3<f32>, 4>,
-  vb : vec3<bool>,
-  ab : array<vec3<bool>, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : __packed_vec3<f32>;
-
-fn f() {
-  var f : S;
-  f.vf = vec3<f32>(buffer);
-  buffer = __packed_vec3<f32>(f.af[0]);
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<PackedVec3>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/pixel_local.cc b/src/tint/lang/msl/writer/ast_raise/pixel_local.cc
deleted file mode 100644
index 572939b..0000000
--- a/src/tint/lang/msl/writer/ast_raise/pixel_local.cc
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/pixel_local.h"
-
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/module.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/utils/containers/transform.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::PixelLocal);
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::PixelLocal::Config);
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-
-/// PIMPL state for the transform
-struct PixelLocal::State {
-    /// The source program
-    const Program& src;
-    /// The target program builder
-    ProgramBuilder b;
-    /// The clone context
-    program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
-    /// The transform config
-    const Config& cfg;
-
-    /// Constructor
-    /// @param program the source program
-    /// @param config the transform config
-    State(const Program& program, const Config& config) : src(program), cfg(config) {}
-
-    /// Runs the transform
-    /// @returns the new program or SkipTransform if the transform is not required
-    ApplyResult Run() {
-        auto& sem = src.Sem();
-
-        // If the pixel local extension isn't enabled, then there must be no use of pixel_local
-        // variables, and so there's nothing for this transform to do.
-        if (!sem.Module()->Extensions().Contains(
-                wgsl::Extension::kChromiumExperimentalPixelLocal)) {
-            return SkipTransform;
-        }
-
-        bool made_changes = false;
-
-        // Change all module scope `var<pixel_local>` variables to `var<private>`.
-        // We need to do this even if the variable is not referenced by the entry point as later
-        // stages do not understand the pixel_local address space.
-        for (auto* global : src.AST().GlobalVariables()) {
-            if (auto* var = global->As<ast::Var>()) {
-                if (sem.Get(var)->AddressSpace() == core::AddressSpace::kPixelLocal) {
-                    // Change the 'var<pixel_local>' to 'var<private>'
-                    ctx.Replace(var->declared_address_space, b.Expr(core::AddressSpace::kPrivate));
-                    made_changes = true;
-                }
-            }
-        }
-
-        /// The pixel local struct
-        Hashset<const sem::Struct*, 1> pixel_local_structs;
-
-        // Find the entry points
-        for (auto* fn : src.AST().Functions()) {
-            if (!fn->IsEntryPoint()) {
-                continue;
-            }
-
-            auto* entry_point = sem.Get(fn);
-
-            // Look for a `var<pixel_local>` used by the entry point...
-            for (auto* global : entry_point->TransitivelyReferencedGlobals()) {
-                if (global->AddressSpace() != core::AddressSpace::kPixelLocal) {
-                    continue;
-                }
-
-                // Obtain struct of the pixel local.
-                auto* pixel_local_str = global->Type()->UnwrapRef()->As<sem::Struct>();
-                if (pixel_local_structs.Add(pixel_local_str)) {
-                    // Add an Color attribute to each member of the pixel_local structure.
-                    for (auto* member : pixel_local_str->Members()) {
-                        ctx.InsertBack(member->Declaration()->attributes,
-                                       b.Color(u32(AttachmentIndex(member->Index()))));
-                        ctx.InsertBack(member->Declaration()->attributes,
-                                       b.Disable(ast::DisabledValidation::kEntryPointParameter));
-                    }
-                }
-
-                TransformEntryPoint(entry_point, global, pixel_local_str);
-                made_changes = true;
-                break;  // Only a single `var<pixel_local>` can be used by an entry point.
-            }
-        }
-
-        if (!made_changes) {
-            return SkipTransform;
-        }
-
-        // At this point, the `var<pixel_local>` will have been replaced with `var<private>`, and
-        // the entry point will use `@color`, which requires the framebuffer fetch extension.
-        // Replace the `chromium_experimental_pixel_local` enable with
-        // `chromium_experimental_framebuffer_fetch`.
-        for (auto* enable : src.AST().Enables()) {
-            for (auto* ext : enable->extensions) {
-                if (ext->name == wgsl::Extension::kChromiumExperimentalPixelLocal) {
-                    ctx.Replace(ext, b.create<ast::Extension>(
-                                         wgsl::Extension::kChromiumExperimentalFramebufferFetch));
-                }
-            }
-        }
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-    /// Transforms the entry point @p entry_point to handle the direct or transitive usage of the
-    /// `var<pixel_local>` @p pixel_local_var.
-    /// @param entry_point the entry point
-    /// @param pixel_local_var the `var<pixel_local>`
-    /// @param pixel_local_str the struct type of the var
-    void TransformEntryPoint(const sem::Function* entry_point,
-                             const sem::GlobalVariable* pixel_local_var,
-                             const sem::Struct* pixel_local_str) {
-        auto* fn = entry_point->Declaration();
-        auto fn_name = fn->name->symbol.Name();
-        auto pixel_local_str_name = ctx.Clone(pixel_local_str->Name());
-        auto pixel_local_var_name = ctx.Clone(pixel_local_var->Declaration()->name->symbol);
-
-        // Remove the @fragment attribute from the entry point
-        ctx.Remove(fn->attributes, ast::GetAttribute<ast::StageAttribute>(fn->attributes));
-        // Rename the entry point
-        auto inner_name = b.Symbols().New(fn_name + "_inner");
-        ctx.Replace(fn->name, b.Ident(inner_name));
-
-        // Create a new function that wraps the entry point.
-        // This function has all the existing entry point parameters and an additional
-        // parameter for the input pixel local structure.
-        auto params = ctx.Clone(fn->params);
-        auto pl_param = b.Symbols().New("pixel_local");
-        params.Push(b.Param(pl_param, b.ty(pixel_local_str_name)));
-
-        // Remove any entry-point attributes from the inner function.
-        // This must come after `ctx.Clone(fn->params)` as we want these attributes on the outer
-        // function.
-        for (auto* param : fn->params) {
-            for (auto* attr : param->attributes) {
-                if (attr->IsAnyOf<ast::BuiltinAttribute, ast::LocationAttribute,
-                                  ast::InterpolateAttribute, ast::InvariantAttribute>()) {
-                    ctx.Remove(param->attributes, attr);
-                }
-            }
-        }
-
-        // Build the outer function's statements, starting with an assignment of the pixel local
-        // parameter to the module scope var.
-        Vector<const ast::Statement*, 3> body{
-            b.Assign(pixel_local_var_name, pl_param),
-        };
-
-        // Build the arguments to call the inner function
-        auto call_args =
-            tint::Transform(fn->params, [&](auto* p) { return b.Expr(ctx.Clone(p->name)); });
-
-        // Create a structure to hold the combined flattened result of the entry point and the pixel
-        // local structure.
-        auto str_name = b.Symbols().New(fn_name + "_res");
-        Vector<const ast::StructMember*, 8> members;
-        Vector<const ast::Expression*, 8> return_args;  // arguments to the final `return` statement
-
-        auto add_member = [&](const core::type::Type* ty, VectorRef<const ast::Attribute*> attrs) {
-            members.Push(b.Member("output_" + std::to_string(members.Length()),
-                                  CreateASTTypeFor(ctx, ty), std::move(attrs)));
-        };
-        for (auto* member : pixel_local_str->Members()) {
-            add_member(member->Type(), Vector{
-                                           b.Location(AInt(AttachmentIndex(member->Index()))),
-                                       });
-            return_args.Push(b.MemberAccessor(pixel_local_var_name, ctx.Clone(member->Name())));
-        }
-        if (fn->return_type) {
-            Symbol call_result = b.Symbols().New("result");
-            if (auto* str = entry_point->ReturnType()->As<sem::Struct>()) {
-                // The entry point returned a structure.
-                for (auto* member : str->Members()) {
-                    auto& member_attrs = member->Declaration()->attributes;
-                    add_member(member->Type(), ctx.Clone(member_attrs));
-                    return_args.Push(b.MemberAccessor(call_result, ctx.Clone(member->Name())));
-                }
-            } else {
-                // The entry point returned a non-structure
-                add_member(entry_point->ReturnType(), ctx.Clone(fn->return_type_attributes));
-                return_args.Push(b.Expr(call_result));
-
-                // Remove the @location from the inner function's return type attributes
-                ctx.Remove(fn->return_type_attributes,
-                           ast::GetAttribute<ast::LocationAttribute>(fn->return_type_attributes));
-            }
-            body.Push(b.Decl(b.Let(call_result, b.Call(inner_name, std::move(call_args)))));
-        } else {
-            body.Push(b.CallStmt(b.Call(inner_name, std::move(call_args))));
-        }
-
-        // Declare the output structure
-        b.Structure(str_name, std::move(members));
-
-        // Return the output structure
-        body.Push(b.Return(b.Call(str_name, std::move(return_args))));
-
-        // Declare the new entry point that calls the inner function
-        b.Func(fn_name, std::move(params), b.ty(str_name), body,
-               Vector{b.Stage(ast::PipelineStage::kFragment)});
-    }
-
-    /// @returns the attachment index for the pixel local field with the given index
-    /// @param field_index the pixel local field index
-    uint32_t AttachmentIndex(uint32_t field_index) {
-        auto idx = cfg.attachments.Get(field_index);
-        if (DAWN_UNLIKELY(!idx)) {
-            b.Diagnostics().AddError(Source{})
-                << "PixelLocal::Config::attachments missing entry for field " << field_index;
-            return 0;
-        }
-        return *idx;
-    }
-};
-
-PixelLocal::PixelLocal() = default;
-
-PixelLocal::~PixelLocal() = default;
-
-ast::transform::Transform::ApplyResult PixelLocal::Apply(const Program& src,
-                                                         const ast::transform::DataMap& inputs,
-                                                         ast::transform::DataMap&) const {
-    auto* cfg = inputs.Get<Config>();
-    if (!cfg) {
-        ProgramBuilder b;
-        b.Diagnostics().AddError(Source{}) << "missing transform data for " << TypeInfo().name;
-        return resolver::Resolve(b);
-    }
-
-    return State(src, *cfg).Run();
-}
-
-PixelLocal::Config::Config() = default;
-
-PixelLocal::Config::Config(const Config&) = default;
-
-PixelLocal::Config::~Config() = default;
-
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/pixel_local.h b/src/tint/lang/msl/writer/ast_raise/pixel_local.h
deleted file mode 100644
index 2fa06d5..0000000
--- a/src/tint/lang/msl/writer/ast_raise/pixel_local.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_MSL_WRITER_AST_RAISE_PIXEL_LOCAL_H_
-#define SRC_TINT_LANG_MSL_WRITER_AST_RAISE_PIXEL_LOCAL_H_
-
-#include <string>
-
-#include "src/tint/lang/wgsl/ast/internal_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/containers/hashmap.h"
-
-namespace tint::msl::writer {
-
-/// PixelLocal transforms module-scope `var<pixel_local>`s and fragment entry point functions that
-/// use them:
-/// * `var<pixel_local>` will be transformed to `var<private>`.
-/// * All of the members of the pixel local struct will have an additional `@color` attribute added.
-/// * The chromium_experimental_pixel_local extension enable will be replaced with an enable for
-///   chromium_experimental_framebuffer_fetch.
-/// * The entry point function will be wrapped with another function ('outer') that calls the
-///  'inner' function.
-/// * The outer function will have an additional parameter of the pixel local struct type, which is
-///   copied to the module-scope var before calling the 'inner' function.
-/// * The outer function will have a new struct return type which holds both the pixel local members
-///   and the returned value(s) of the 'inner' function.
-class PixelLocal final : public Castable<PixelLocal, ast::transform::Transform> {
-  public:
-    /// Transform configuration options
-    struct Config final : public Castable<Config, ast::transform::Data> {
-        /// Constructor
-        Config();
-
-        /// Copy Constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// Index of pixel_local structure member index to attachment index
-        Hashmap<uint32_t, uint32_t, 8> attachments;
-    };
-
-    /// Constructor
-    PixelLocal();
-
-    /// Destructor
-    ~PixelLocal() override;
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::msl::writer
-
-#endif  // SRC_TINT_LANG_MSL_WRITER_AST_RAISE_PIXEL_LOCAL_H_
diff --git a/src/tint/lang/msl/writer/ast_raise/pixel_local_test.cc b/src/tint/lang/msl/writer/ast_raise/pixel_local_test.cc
deleted file mode 100644
index 09776c1..0000000
--- a/src/tint/lang/msl/writer/ast_raise/pixel_local_test.cc
+++ /dev/null
@@ -1,1121 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/pixel_local.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-struct Binding {
-    uint32_t field_index;
-    uint32_t attachment_index;
-};
-
-ast::transform::DataMap Bindings(std::initializer_list<Binding> bindings) {
-    PixelLocal::Config cfg;
-    for (auto& binding : bindings) {
-        cfg.attachments.Add(binding.field_index, binding.attachment_index);
-    }
-    ast::transform::DataMap data;
-    data.Add<PixelLocal::Config>(std::move(cfg));
-    return data;
-}
-
-using PixelLocalTest = ast::transform::TransformTest;
-
-TEST_F(PixelLocalTest, EmptyModule) {
-    auto* src = "";
-
-    EXPECT_FALSE(ShouldRun<PixelLocal>(src, Bindings({})));
-}
-
-TEST_F(PixelLocalTest, Var) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : i32,
-};
-
-var<pixel_local> P : PixelLocal;
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct PixelLocal {
-  a : i32,
-}
-
-var<private> P : PixelLocal;
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, UsedInSingleEntryPoint) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F() {
-  P.a += 42;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner();
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner() {
-  P.a += 42;
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, SameVarUsedInMultipleEntryPoints) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F1() {
-  P.a += 10;
-}
-
-@fragment
-fn F2() {
-  P.a += 20;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F1_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F1(pixel_local_1 : PixelLocal) -> F1_res {
-  P = pixel_local_1;
-  F1_inner();
-  return F1_res(P.a);
-}
-
-struct F2_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F2(pixel_local_2 : PixelLocal) -> F2_res {
-  P = pixel_local_2;
-  F2_inner();
-  return F2_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F1_inner() {
-  P.a += 10;
-}
-
-fn F2_inner() {
-  P.a += 20;
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, DifferentVarUsedInMultipleEntryPoints) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P1 : PixelLocal;
-var<pixel_local> P2 : PixelLocal;
-
-@fragment
-fn F1() {
-  P1.a += 10;
-}
-
-@fragment
-fn F2() {
-  P2.a += 20;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F1_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F1(pixel_local_1 : PixelLocal) -> F1_res {
-  P1 = pixel_local_1;
-  F1_inner();
-  return F1_res(P1.a);
-}
-
-struct F2_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F2(pixel_local_2 : PixelLocal) -> F2_res {
-  P2 = pixel_local_2;
-  F2_inner();
-  return F2_res(P2.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P1 : PixelLocal;
-
-var<private> P2 : PixelLocal;
-
-fn F1_inner() {
-  P1.a += 10;
-}
-
-fn F2_inner() {
-  P2.a += 20;
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, DifferentStructUsedInMultipleEntryPoints) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal1 {
-  a : u32,
-}
-
-struct PixelLocal2 {
-  a : u32,
-}
-
-var<pixel_local> P1 : PixelLocal1;
-var<pixel_local> P2 : PixelLocal2;
-
-@fragment
-fn F1() {
-  P1.a += 10;
-}
-
-@fragment
-fn F2() {
-  P2.a += 20;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F1_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F1(pixel_local_1 : PixelLocal1) -> F1_res {
-  P1 = pixel_local_1;
-  F1_inner();
-  return F1_res(P1.a);
-}
-
-struct F2_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F2(pixel_local_2 : PixelLocal2) -> F2_res {
-  P2 = pixel_local_2;
-  F2_inner();
-  return F2_res(P2.a);
-}
-
-struct PixelLocal1 {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-struct PixelLocal2 {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P1 : PixelLocal1;
-
-var<private> P2 : PixelLocal2;
-
-fn F1_inner() {
-  P1.a += 10;
-}
-
-fn F2_inner() {
-  P2.a += 20;
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, UseInCallee) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-fn X() {
-  P.a += 42;
-}
-
-@fragment
-fn F() {
-  X();
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner();
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn X() {
-  P.a += 42;
-}
-
-fn F_inner() {
-  X();
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, MultipleAttachments) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-  b : i32,
-  c : f32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F() {
-  P.a = 42;
-  P.b = i32(P.c);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-  @location(0)
-  output_1 : i32,
-  @location(10)
-  output_2 : f32,
-}
-
-@fragment
-fn F(pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner();
-  return F_res(P.a, P.b, P.c);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-  @color(0u) @internal(disable_validation__entry_point_parameter)
-  b : i32,
-  @color(10u) @internal(disable_validation__entry_point_parameter)
-  c : f32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner() {
-  P.a = 42;
-  P.b = i32(P.c);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}, {1, 0}, {2, 10}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithBuiltinInputParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@builtin(position) pos : vec4f) {
-  P.a += u32(pos.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(@builtin(position) pos : vec4f, pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner(pos);
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(pos : vec4f) {
-  P.a += u32(pos.x);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithInvariantBuiltinInputParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@invariant @builtin(position) pos : vec4f) {
-  P.a += u32(pos.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(@invariant @builtin(position) pos : vec4f, pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner(pos);
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(pos : vec4f) {
-  P.a += u32(pos.x);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithBuiltinInputStructParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct In {
-  @builtin(position) pos : vec4f,
-}
-
-@fragment
-fn F(in : In) {
-  P.a += u32(in.pos.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(in : In, pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner(in);
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct In {
-  @builtin(position)
-  pos : vec4f,
-}
-
-fn F_inner(in : In) {
-  P.a += u32(in.pos.x);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithInvariantBuiltinInputStructParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct In {
-  @invariant @builtin(position) pos : vec4f,
-}
-
-@fragment
-fn F(in : In) {
-  P.a += u32(in.pos.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(in : In, pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner(in);
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct In {
-  @invariant @builtin(position)
-  pos : vec4f,
-}
-
-fn F_inner(in : In) {
-  P.a += u32(in.pos.x);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithLocationInputParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@location(0) a : vec4f, @interpolate(flat) @location(1) b : vec4f) {
-  P.a += u32(a.x) + u32(b.y);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(@location(0) a : vec4f, @interpolate(flat) @location(1) b : vec4f, pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner(a, b);
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(a : vec4f, b : vec4f) {
-  P.a += (u32(a.x) + u32(b.y));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithLocationInputStructParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct In {
-  @location(0) a : vec4f,
-  @interpolate(flat) @location(1) b : vec4f,
-}
-
-@fragment
-fn F(in : In) {
-  P.a += u32(in.a.x) + u32(in.b.y);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(in : In, pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner(in);
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct In {
-  @location(0)
-  a : vec4f,
-  @interpolate(flat) @location(1)
-  b : vec4f,
-}
-
-fn F_inner(in : In) {
-  P.a += (u32(in.a.x) + u32(in.b.y));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithBuiltinAndLocationInputParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@builtin(position) pos : vec4f, @location(0) uv : vec4f) {
-  P.a += u32(pos.x) + u32(uv.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(@builtin(position) pos : vec4f, @location(0) uv : vec4f, pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner(pos, uv);
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(pos : vec4f, uv : vec4f) {
-  P.a += (u32(pos.x) + u32(uv.x));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithBuiltinAndLocationInputStructParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct In {
-  @builtin(position) pos : vec4f,
-  @location(0) uv : vec4f,
-}
-
-@fragment
-fn F(in : In) {
-  P.a += u32(in.pos.x) + u32(in.uv.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-}
-
-@fragment
-fn F(in : In, pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  F_inner(in);
-  return F_res(P.a);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct In {
-  @builtin(position)
-  pos : vec4f,
-  @location(0)
-  uv : vec4f,
-}
-
-fn F_inner(in : In) {
-  P.a += (u32(in.pos.x) + u32(in.uv.x));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithSingleFragmentOutput) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F() -> @location(0) vec4f {
-  P.a += 42;
-  return vec4f(1);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-  @location(0)
-  output_1 : vec4<f32>,
-}
-
-@fragment
-fn F(pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  let result = F_inner();
-  return F_res(P.a, result);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner() -> vec4f {
-  P.a += 42;
-  return vec4f(1);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, WithMultipleFragmentOutputs) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-  b : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct Output {
-  @location(0) x : vec4f,
-  @location(2) y : vec4f,
-}
-
-@fragment
-fn F() -> Output {
-  P.a += 42;
-  return Output(vec4f(1), vec4f(9));
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F_res {
-  @location(1)
-  output_0 : u32,
-  @location(5)
-  output_1 : u32,
-  @location(0)
-  output_2 : vec4<f32>,
-  @location(2)
-  output_3 : vec4<f32>,
-}
-
-@fragment
-fn F(pixel_local_1 : PixelLocal) -> F_res {
-  P = pixel_local_1;
-  let result = F_inner();
-  return F_res(P.a, P.b, result.x, result.y);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-  @color(5u) @internal(disable_validation__entry_point_parameter)
-  b : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct Output {
-  @location(0)
-  x : vec4f,
-  @location(2)
-  y : vec4f,
-}
-
-fn F_inner() -> Output {
-  P.a += 42;
-  return Output(vec4f(1), vec4f(9));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}, {1, 5}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PixelLocalTest, OutputStructUsedByMultipleEntryPoints) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-  b : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct Output {
-  @location(0) x : vec4f,
-  @location(2) y : vec4f,
-}
-
-@fragment
-fn F1() -> Output {
-  P.a += 42;
-  return Output(vec4f(1), vec4f(9));
-}
-
-@fragment
-fn F2() -> Output {
-  return Output(vec4f(1), vec4f(9));
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct F1_res {
-  @location(1)
-  output_0 : u32,
-  @location(5)
-  output_1 : u32,
-  @location(0)
-  output_2 : vec4<f32>,
-  @location(2)
-  output_3 : vec4<f32>,
-}
-
-@fragment
-fn F1(pixel_local_1 : PixelLocal) -> F1_res {
-  P = pixel_local_1;
-  let result = F1_inner();
-  return F1_res(P.a, P.b, result.x, result.y);
-}
-
-struct PixelLocal {
-  @color(1u) @internal(disable_validation__entry_point_parameter)
-  a : u32,
-  @color(5u) @internal(disable_validation__entry_point_parameter)
-  b : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct Output {
-  @location(0)
-  x : vec4f,
-  @location(2)
-  y : vec4f,
-}
-
-fn F1_inner() -> Output {
-  P.a += 42;
-  return Output(vec4f(1), vec4f(9));
-}
-
-@fragment
-fn F2() -> Output {
-  return Output(vec4f(1), vec4f(9));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1}, {1, 5}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/quad_swap.cc b/src/tint/lang/msl/writer/ast_raise/quad_swap.cc
deleted file mode 100644
index 0afe1a0..0000000
--- a/src/tint/lang/msl/writer/ast_raise/quad_swap.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2024 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/quad_swap.h"
-
-#include <utility>
-
-#include "src/tint/lang/core/builtin_fn.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/lang/wgsl/builtin_fn.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::QuadSwap);
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::QuadSwap::QuadShuffle);
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::QuadSwap::ThreadIndexInQuadgroup);
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-
-/// PIMPL state for the transform
-struct QuadSwap::State {
-    /// The source program
-    const Program& src;
-    /// The target program builder
-    ProgramBuilder b;
-    /// The clone context
-    program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
-
-    /// The set of names for the tint_msl_quadSwap* helper functions.
-    Hashmap<std::pair<wgsl::BuiltinFn, const core::type::Type*>, Symbol, 8> quad_swap_helpers;
-
-    /// The set of names for the tint_msl_quad_shuffle* intrinsic functions.
-    Hashmap<const core::type::Type*, Symbol, 8> quad_shuffle_intrinsics;
-
-    /// The name of the `tint_msl_thread_index_in_quadgroup` global variable.
-    Symbol thread_index_in_quadgroup{};
-
-    /// The set of a functions that directly call `quadSwap*()` builtin functions.
-    Hashset<const sem::Function*, 4> quad_swap_callers;
-
-    /// Constructor
-    /// @param program the source program
-    explicit State(const Program& program) : src(program) {}
-
-    /// Runs the transform
-    /// @returns the new program or SkipTransform if the transform is not required
-    ApplyResult Run() {
-        auto& sem = src.Sem();
-
-        bool made_changes = false;
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
-            auto* call = sem.Get<sem::Call>(node);
-            if (call) {
-                // If this is a call to a `quadSwap*()` builtin, replace it with a call to the
-                // helper function and make a note of the function that we are in.
-                auto* builtin = call->Target()->As<sem::BuiltinFn>();
-                if (builtin && builtin->IsQuadSwap()) {
-                    auto* expr = ctx.Clone(call->Arguments()[0]->Declaration());
-                    ctx.Replace(call->Declaration(),
-                                b.Call(GetHelper(builtin->Fn(), call->Type()), expr));
-                    quad_swap_callers.Add(call->Stmt()->Function());
-                    made_changes = true;
-                }
-            }
-        }
-        if (!made_changes) {
-            return SkipTransform;
-        }
-
-        // Set the thread_index_in_quadgroup global variable at the start of each entry point that
-        // transitively calls the `quadSwap*()` builtin function.
-        for (auto* global : src.AST().GlobalDeclarations()) {
-            auto* func = global->As<ast::Function>();
-            if (func && func->IsEntryPoint() && TransitvelyCallsQuadSwap(sem.Get(func))) {
-                SetThreadIndexInQuadgroup(func);
-            }
-        }
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-    /// Get or create the appropriate `tint_msl_quadSwap*` helper function for the given builtin
-    /// function and return type.
-    /// @returns the name of the helper function
-    Symbol GetHelper(wgsl::BuiltinFn func, const core::type::Type* type) {
-        return quad_swap_helpers.GetOrAdd(std::make_pair(func, type), [&] {
-            Symbol quad_swap_helper = b.Symbols().New(std::string("tint_msl_") + str(func));
-            Symbol intrinsic = GetIntrinsic(type);
-
-            // Declare the `tint_msl_quadSwap*` helper function as follows:
-            //   fn tint_msl_quadSwapFnName(e : T) -> T {
-            //     tint_msl_quad_shuffle(e, tint_msl_thread_index_in_quadgroup ^ rhs)
-            //   }
-            // where rhs is determined based on the builtin function call:
-            // +------------------+------+
-            // |       func       | rhs  |
-            // +------------------+------+
-            // | quadSwapX        | 0b1  |
-            // | quadSwapY        | 0b10 |
-            // | quadSwapDiagonal | 0b11 |
-            // +------------------+------+
-            auto* expr = b.Param("e", CreateASTTypeFor(ctx, type));
-            core::u32 rhs;
-            switch (func) {
-                case wgsl::BuiltinFn::kQuadSwapX:
-                    rhs = 0b1;
-                    break;
-                case wgsl::BuiltinFn::kQuadSwapY:
-                    rhs = 0b10;
-                    break;
-                case wgsl::BuiltinFn::kQuadSwapDiagonal:
-                    rhs = 0b11;
-                    break;
-                default:
-                    TINT_UNREACHABLE() << "unsupported builtin function";
-            }
-            b.Func(quad_swap_helper, Vector{expr}, CreateASTTypeFor(ctx, type),
-                   Vector{
-                       b.Return(b.Call(intrinsic, expr, b.Xor(GetGlobalVar(), rhs))),
-                   });
-            return quad_swap_helper;
-        });
-    }
-
-    /// Get or create the `tint_msl_quad_shuffle` intrinsic placeholder function for the given
-    /// return type.
-    /// @returns the name of the function
-    Symbol GetIntrinsic(const core::type::Type* type) {
-        return quad_shuffle_intrinsics.GetOrAdd(type, [&] {
-            auto intrinsic = b.Symbols().New(std::string("tint_msl_quad_shuffle"));
-
-            // Declare the `tint_msl_quad_shuffle` function, which will be replaced by the MSL
-            // `quad_shuffle` intrinsic function to perform the swap.
-            {
-                auto* data = b.Param("data", CreateASTTypeFor(ctx, type));
-                auto* quad_lane_id = b.Param("quad_lane_id", b.ty.u32());
-                b.Func(intrinsic, Vector{data, quad_lane_id}, CreateASTTypeFor(ctx, type), nullptr,
-                       Vector{b.ASTNodes().Create<QuadShuffle>(b.ID(), b.AllocateNodeID()),
-                              b.Disable(ast::DisabledValidation::kFunctionHasNoBody)});
-            }
-
-            return intrinsic;
-        });
-    }
-
-    /// Get or create the `tint_msl_thread_index_in_quadgroup` global variable.
-    /// @returns the name of the variable
-    Symbol GetGlobalVar() {
-        if (!thread_index_in_quadgroup) {
-            thread_index_in_quadgroup = b.Symbols().New("tint_msl_thread_index_in_quadgroup");
-
-            // Declare the `tint_msl_thread_index_in_quadgroup` variable.
-            b.GlobalVar(thread_index_in_quadgroup, core::AddressSpace::kPrivate, b.ty.u32());
-        }
-
-        return thread_index_in_quadgroup;
-    }
-
-    /// Check if a function directly or transitively calls a `quadSwap*()` builtin.
-    /// @param func the function to check
-    /// @returns true if the function transitively calls `quadSwap*()`
-    bool TransitvelyCallsQuadSwap(const sem::Function* func) {
-        if (quad_swap_callers.Contains(func)) {
-            return true;
-        }
-        for (auto* called : func->TransitivelyCalledFunctions()) {
-            if (quad_swap_callers.Contains(called)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /// Add code to set the `thread_index_in_quadgroup` variable at the start of an entry point.
-    /// @param ep the entry point
-    void SetThreadIndexInQuadgroup(const ast::Function* ep) {
-        // Add the entry point parameter with an attribute to indicate to the the MSL backend that
-        // it should be annotated with [[thread_index_in_quadgroup]].
-        Symbol thread_index_in_quadgroup_param = b.Symbols().New("tint_thread_index_in_quadgroup");
-        ctx.InsertBack(ep->params, b.Param(thread_index_in_quadgroup_param, b.ty.u32(),
-                                           Vector{b.ASTNodes().Create<ThreadIndexInQuadgroup>(
-                                               b.ID(), b.AllocateNodeID())}));
-
-        // Add the following to the top of the entry point:
-        // {
-        //   tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-        // }
-        auto* block = b.Block(Vector{
-            b.Assign(thread_index_in_quadgroup, thread_index_in_quadgroup_param),
-        });
-        ctx.InsertFront(ep->body->statements, block);
-    }
-};
-
-QuadSwap::QuadSwap() = default;
-
-QuadSwap::~QuadSwap() = default;
-
-ast::transform::Transform::ApplyResult QuadSwap::Apply(const Program& src,
-                                                       const ast::transform::DataMap&,
-                                                       ast::transform::DataMap&) const {
-    return State(src).Run();
-}
-
-QuadSwap::QuadShuffle::~QuadShuffle() = default;
-
-const QuadSwap::QuadShuffle* QuadSwap::QuadShuffle::Clone(ast::CloneContext& ctx) const {
-    return ctx.dst->ASTNodes().Create<QuadSwap::QuadShuffle>(ctx.dst->ID(),
-                                                             ctx.dst->AllocateNodeID());
-}
-
-QuadSwap::ThreadIndexInQuadgroup::~ThreadIndexInQuadgroup() = default;
-
-const QuadSwap::ThreadIndexInQuadgroup* QuadSwap::ThreadIndexInQuadgroup::Clone(
-    ast::CloneContext& ctx) const {
-    return ctx.dst->ASTNodes().Create<QuadSwap::ThreadIndexInQuadgroup>(ctx.dst->ID(),
-                                                                        ctx.dst->AllocateNodeID());
-}
-
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/quad_swap.h b/src/tint/lang/msl/writer/ast_raise/quad_swap.h
deleted file mode 100644
index 1654ad4..0000000
--- a/src/tint/lang/msl/writer/ast_raise/quad_swap.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2024 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_MSL_WRITER_AST_RAISE_QUAD_SWAP_H_
-#define SRC_TINT_LANG_MSL_WRITER_AST_RAISE_QUAD_SWAP_H_
-
-#include <string>
-
-#include "src/tint/lang/wgsl/ast/internal_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::msl::writer {
-
-/// QuadSwap is a transform that replaces calls to `quadSwap{X, Y, Diagonal}()` with an
-/// implementation that uses MSL's `quad_shuffle()` using the following mapping:
-/// +------------------+------------------------------------------------------------------+
-/// |       WGSL       |                               MSL                                |
-/// +------------------+------------------------------------------------------------------+
-/// | quadSwapX        | quad_shuffle with quad_lane_id=thread_index_in_quad_group ^ 0b1  |
-/// | quadSwapY        | quad_shuffle with quad_lane_id=thread_index_in_quad_group ^ 0b10 |
-/// | quadSwapDiagonal | quad_shuffle with quad_lane_id=thread_index_in_quad_group ^ 0b11 |
-/// +------------------+------------------------------------------------------------------+
-///
-/// @note Depends on the following transforms to have been run first:
-/// * CanonicalizeEntryPointIO
-class QuadSwap final : public Castable<QuadSwap, ast::transform::Transform> {
-  public:
-    /// Constructor
-    QuadSwap();
-
-    /// Destructor
-    ~QuadSwap() override;
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-
-    /// QuadShuffle is an InternalAttribute that is used to decorate a stub function so
-    /// that the MSL backend transforms this into calls to the `quad_shuffle` function.
-    class QuadShuffle final : public Castable<QuadShuffle, ast::InternalAttribute> {
-      public:
-        /// Constructor
-        /// @param pid the identifier of the program that owns this node
-        /// @param nid the unique node identifier
-        QuadShuffle(GenerationID pid, ast::NodeID nid) : Base(pid, nid, Empty) {}
-
-        /// Destructor
-        ~QuadShuffle() override;
-
-        /// @copydoc ast::InternalAttribute::InternalName
-        std::string InternalName() const override { return "quad_shuffle"; }
-
-        /// Performs a deep clone of this object using the program::CloneContext `ctx`.
-        /// @param ctx the clone context
-        /// @return the newly cloned object
-        const QuadShuffle* Clone(ast::CloneContext& ctx) const override;
-    };
-
-    /// ThreadIndexInQuadgroup is an InternalAttribute that is used to decorate an entrypoint
-    /// parameter so that the MSL backend transforms this into a `[[thread_index_in_quadgroup]]`
-    /// attribute.
-    class ThreadIndexInQuadgroup final
-        : public Castable<ThreadIndexInQuadgroup, ast::InternalAttribute> {
-      public:
-        /// Constructor
-        /// @param pid the identifier of the program that owns this node
-        /// @param nid the unique node identifier
-        ThreadIndexInQuadgroup(GenerationID pid, ast::NodeID nid) : Base(pid, nid, Empty) {}
-
-        /// Destructor
-        ~ThreadIndexInQuadgroup() override;
-
-        /// @copydoc ast::InternalAttribute::InternalName
-        std::string InternalName() const override { return "thread_index_in_quadgroup"; }
-
-        /// Performs a deep clone of this object using the program::CloneContext `ctx`.
-        /// @param ctx the clone context
-        /// @return the newly cloned object
-        const ThreadIndexInQuadgroup* Clone(ast::CloneContext& ctx) const override;
-    };
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::msl::writer
-
-#endif  // SRC_TINT_LANG_MSL_WRITER_AST_RAISE_QUAD_SWAP_H_
diff --git a/src/tint/lang/msl/writer/ast_raise/quad_swap_test.cc b/src/tint/lang/msl/writer/ast_raise/quad_swap_test.cc
deleted file mode 100644
index f24f48e..0000000
--- a/src/tint/lang/msl/writer/ast_raise/quad_swap_test.cc
+++ /dev/null
@@ -1,376 +0,0 @@
-// Copyright 2024 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/quad_swap.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using QuadSwapTest = ast::transform::TransformTest;
-
-TEST_F(QuadSwapTest, EmptyModule) {
-    auto* src = "";
-
-    EXPECT_FALSE(ShouldRun<QuadSwap>(src));
-}
-
-TEST_F(QuadSwapTest, DirectUseQuadSwapX) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: f32 = quadSwapX(1.f);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle(data : f32, quad_lane_id : u32) -> f32
-
-var<private> tint_msl_thread_index_in_quadgroup : u32;
-
-fn tint_msl_quadSwapX(e : f32) -> f32 {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 1u));
-}
-
-@compute @workgroup_size(64)
-fn foo(@internal(thread_index_in_quadgroup) tint_thread_index_in_quadgroup : u32) {
-  {
-    tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-  }
-  let x : f32 = tint_msl_quadSwapX(1.0f);
-}
-)";
-
-    auto got = Run<QuadSwap>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(QuadSwapTest, IndirectUseQuadSwapX) {
-    auto* src = R"(
-enable subgroups;
-
-fn bar() -> vec4u {
-  let expr = vec4u(1u, 1u, 1u, 1u);
-  return quadSwapX(expr);
-}
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: vec4u = bar();
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle(data : vec4<u32>, quad_lane_id : u32) -> vec4<u32>
-
-var<private> tint_msl_thread_index_in_quadgroup : u32;
-
-fn tint_msl_quadSwapX(e : vec4<u32>) -> vec4<u32> {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 1u));
-}
-
-fn bar() -> vec4u {
-  let expr = vec4u(1u, 1u, 1u, 1u);
-  return tint_msl_quadSwapX(expr);
-}
-
-@compute @workgroup_size(64)
-fn foo(@internal(thread_index_in_quadgroup) tint_thread_index_in_quadgroup : u32) {
-  {
-    tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-  }
-  let x : vec4u = bar();
-}
-)";
-
-    auto got = Run<QuadSwap>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(QuadSwapTest, DirectUseQuadSwapY) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: i32 = quadSwapY(1i);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle(data : i32, quad_lane_id : u32) -> i32
-
-var<private> tint_msl_thread_index_in_quadgroup : u32;
-
-fn tint_msl_quadSwapY(e : i32) -> i32 {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 2u));
-}
-
-@compute @workgroup_size(64)
-fn foo(@internal(thread_index_in_quadgroup) tint_thread_index_in_quadgroup : u32) {
-  {
-    tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-  }
-  let x : i32 = tint_msl_quadSwapY(1i);
-}
-)";
-
-    auto got = Run<QuadSwap>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(QuadSwapTest, DirectUseQuadSwapDiagonal) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: i32 = quadSwapDiagonal(1i);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle(data : i32, quad_lane_id : u32) -> i32
-
-var<private> tint_msl_thread_index_in_quadgroup : u32;
-
-fn tint_msl_quadSwapDiagonal(e : i32) -> i32 {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 3u));
-}
-
-@compute @workgroup_size(64)
-fn foo(@internal(thread_index_in_quadgroup) tint_thread_index_in_quadgroup : u32) {
-  {
-    tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-  }
-  let x : i32 = tint_msl_quadSwapDiagonal(1i);
-}
-)";
-
-    auto got = Run<QuadSwap>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(QuadSwapTest, MultipleCallsSameFunction) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: u32 = quadSwapX(1u);
-  let y: u32 = quadSwapX(1u);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle(data : u32, quad_lane_id : u32) -> u32
-
-var<private> tint_msl_thread_index_in_quadgroup : u32;
-
-fn tint_msl_quadSwapX(e : u32) -> u32 {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 1u));
-}
-
-@compute @workgroup_size(64)
-fn foo(@internal(thread_index_in_quadgroup) tint_thread_index_in_quadgroup : u32) {
-  {
-    tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-  }
-  let x : u32 = tint_msl_quadSwapX(1u);
-  let y : u32 = tint_msl_quadSwapX(1u);
-}
-)";
-
-    auto got = Run<QuadSwap>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(QuadSwapTest, MultipleCallsSameFunctionDifferentType) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: u32 = quadSwapX(1u);
-  let y: i32 = quadSwapX(1i);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle(data : u32, quad_lane_id : u32) -> u32
-
-var<private> tint_msl_thread_index_in_quadgroup : u32;
-
-fn tint_msl_quadSwapX(e : u32) -> u32 {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 1u));
-}
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle_1(data : i32, quad_lane_id : u32) -> i32
-
-fn tint_msl_quadSwapX_1(e : i32) -> i32 {
-  return tint_msl_quad_shuffle_1(e, (tint_msl_thread_index_in_quadgroup ^ 1u));
-}
-
-@compute @workgroup_size(64)
-fn foo(@internal(thread_index_in_quadgroup) tint_thread_index_in_quadgroup : u32) {
-  {
-    tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-  }
-  let x : u32 = tint_msl_quadSwapX(1u);
-  let y : i32 = tint_msl_quadSwapX_1(1i);
-}
-)";
-
-    auto got = Run<QuadSwap>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(QuadSwapTest, MultipleCallsSameTypeDifferentFunction) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: u32 = quadSwapX(1u);
-  let y: u32 = quadSwapY(1u);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle(data : u32, quad_lane_id : u32) -> u32
-
-var<private> tint_msl_thread_index_in_quadgroup : u32;
-
-fn tint_msl_quadSwapX(e : u32) -> u32 {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 1u));
-}
-
-fn tint_msl_quadSwapY(e : u32) -> u32 {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 2u));
-}
-
-@compute @workgroup_size(64)
-fn foo(@internal(thread_index_in_quadgroup) tint_thread_index_in_quadgroup : u32) {
-  {
-    tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-  }
-  let x : u32 = tint_msl_quadSwapX(1u);
-  let y : u32 = tint_msl_quadSwapY(1u);
-}
-)";
-
-    auto got = Run<QuadSwap>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(QuadSwapTest, MultipleCallsDifferentTypeDifferentFunction) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: i32 = quadSwapX(1i);
-  let y: u32 = quadSwapY(1u);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle(data : i32, quad_lane_id : u32) -> i32
-
-var<private> tint_msl_thread_index_in_quadgroup : u32;
-
-fn tint_msl_quadSwapX(e : i32) -> i32 {
-  return tint_msl_quad_shuffle(e, (tint_msl_thread_index_in_quadgroup ^ 1u));
-}
-
-@internal(quad_shuffle) @internal(disable_validation__function_has_no_body)
-fn tint_msl_quad_shuffle_1(data : u32, quad_lane_id : u32) -> u32
-
-fn tint_msl_quadSwapY(e : u32) -> u32 {
-  return tint_msl_quad_shuffle_1(e, (tint_msl_thread_index_in_quadgroup ^ 2u));
-}
-
-@compute @workgroup_size(64)
-fn foo(@internal(thread_index_in_quadgroup) tint_thread_index_in_quadgroup : u32) {
-  {
-    tint_msl_thread_index_in_quadgroup = tint_thread_index_in_quadgroup;
-  }
-  let x : i32 = tint_msl_quadSwapX(1i);
-  let y : u32 = tint_msl_quadSwapY(1u);
-}
-)";
-
-    auto got = Run<QuadSwap>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/subgroup_ballot.cc b/src/tint/lang/msl/writer/ast_raise/subgroup_ballot.cc
deleted file mode 100644
index 6a93177..0000000
--- a/src/tint/lang/msl/writer/ast_raise/subgroup_ballot.cc
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/subgroup_ballot.h"
-
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::SubgroupBallot);
-TINT_INSTANTIATE_TYPEINFO(tint::msl::writer::SubgroupBallot::SimdBallot);
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::msl::writer {
-
-/// PIMPL state for the transform
-struct SubgroupBallot::State {
-    /// The source program
-    const Program& src;
-    /// The target program builder
-    ProgramBuilder b;
-    /// The clone context
-    program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
-
-    /// The name of the `tint_subgroup_ballot` helper function.
-    Symbol ballot_helper{};
-
-    /// The name of the `tint_subgroup_size_mask` global variable.
-    Symbol subgroup_size_mask{};
-
-    /// The set of a functions that directly call `subgroupBallot()`.
-    Hashset<const sem::Function*, 4> ballot_callers;
-
-    /// Constructor
-    /// @param program the source program
-    explicit State(const Program& program) : src(program) {}
-
-    /// Runs the transform
-    /// @returns the new program or SkipTransform if the transform is not required
-    ApplyResult Run() {
-        auto& sem = src.Sem();
-
-        bool made_changes = false;
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
-            auto* call = sem.Get<sem::Call>(node);
-            if (call) {
-                // If this is a call to a `subgroupBallot()` builtin, replace it with a call to the
-                // helper function and make a note of the function that we are in.
-                auto* builtin = call->Target()->As<sem::BuiltinFn>();
-                if (builtin && builtin->Fn() == wgsl::BuiltinFn::kSubgroupBallot) {
-                    auto* pred = ctx.Clone(call->Arguments()[0]->Declaration());
-                    ctx.Replace(call->Declaration(), b.Call(GetHelper(), pred));
-                    ballot_callers.Add(call->Stmt()->Function());
-                    made_changes = true;
-                }
-            }
-        }
-        if (!made_changes) {
-            return SkipTransform;
-        }
-
-        // Set the subgroup size mask at the start of each entry point that transitively calls
-        // `subgroupBallot()`.
-        for (auto* global : src.AST().GlobalDeclarations()) {
-            auto* func = global->As<ast::Function>();
-            if (func && func->IsEntryPoint() && TransitvelyCallsSubgroupBallot(sem.Get(func))) {
-                SetSubgroupSizeMask(func);
-            }
-        }
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-    /// Get (or create) the `tint_msl_subgroup_ballot` helper function.
-    /// @returns the name of the helper function
-    Symbol GetHelper() {
-        if (!ballot_helper) {
-            auto intrinsic = b.Symbols().New("tint_msl_simd_ballot");
-            subgroup_size_mask = b.Symbols().New("tint_subgroup_size_mask");
-            ballot_helper = b.Symbols().New("tint_msl_subgroup_ballot");
-
-            // Declare the `tint_msl_subgroup_ballot` intrinsic function, which will use the
-            // `simd_ballot` function to return 64-bit vote.
-            {
-                auto* pred = b.Param("pred", b.ty.bool_());
-                b.Func(intrinsic, Vector{pred}, b.ty.vec2<u32>(), nullptr,
-                       Vector{b.ASTNodes().Create<SimdBallot>(b.ID(), b.AllocateNodeID()),
-                              b.Disable(ast::DisabledValidation::kFunctionHasNoBody)});
-            }
-
-            // Declare the `tint_subgroup_size_mask` variable.
-            b.GlobalVar(subgroup_size_mask, core::AddressSpace::kPrivate, b.ty.vec4<u32>());
-
-            // Declare the `tint_msl_subgroup_ballot` helper function as follows:
-            //   fn tint_msl_subgroup_ballot(pred : bool) -> vec4u {
-            //     let vote : vec2u = vec4f(tint_simd_ballot(pred), 0, 0);
-            //     return (vote & tint_subgroup_size_mask);
-            //   }
-            auto* pred = b.Param("pred", b.ty.bool_());
-            auto* vote =
-                b.Let(b.Sym(), b.Call(b.ty.vec4<u32>(), b.Call(intrinsic, pred), 0_u, 0_u));
-            b.Func(ballot_helper, Vector{pred}, b.ty.vec4<u32>(),
-                   Vector{
-                       b.Decl(vote),
-                       b.Return(b.And(vote, subgroup_size_mask)),
-                   });
-        }
-        return ballot_helper;
-    }
-
-    /// Check if a function directly or transitively calls the `subgroupBallot()` builtin.
-    /// @param func the function to check
-    /// @returns true if the function transitively calls `subgroupBallot()`
-    bool TransitvelyCallsSubgroupBallot(const sem::Function* func) {
-        if (ballot_callers.Contains(func)) {
-            return true;
-        }
-        for (auto* called : func->TransitivelyCalledFunctions()) {
-            if (ballot_callers.Contains(called)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /// Add code to set the `subgroup_size_mask` variable at the start of an entry point.
-    /// @param ep the entry point
-    void SetSubgroupSizeMask(const ast::Function* ep) {
-        // Check the entry point parameters for an existing `subgroup_size` builtin.
-        Symbol subgroup_size;
-        for (auto* param : ep->params) {
-            auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes);
-            if (builtin && builtin->builtin == core::BuiltinValue::kSubgroupSize) {
-                subgroup_size = ctx.Clone(param->name->symbol);
-            }
-        }
-        if (!subgroup_size.IsValid()) {
-            // No `subgroup_size` builtin parameter was found, so add one.
-            subgroup_size = b.Symbols().New("tint_subgroup_size");
-            ctx.InsertBack(ep->params, b.Param(subgroup_size, b.ty.u32(),
-                                               Vector{
-                                                   b.Builtin(core::BuiltinValue::kSubgroupSize),
-                                               }));
-        }
-
-        // Add the following to the top of the entry point:
-        // {
-        //   let gt = subgroup_size > 32;
-        //   subgroup_size_mask[0] = select(0xffffffff >> (32 - subgroup_size), 0xffffffff, gt);
-        //   subgroup_size_mask[1] = select(0, 0xffffffff >> (64 - subgroup_size), gt);
-        // }
-        auto* gt = b.Let(b.Sym("gt"), b.GreaterThan(subgroup_size, 32_u));
-        auto* lo =
-            b.Call("select", b.Shr(0xffffffff_u, b.Sub(32_u, subgroup_size)), 0xffffffff_u, gt);
-        auto* hi = b.Call("select", 0_u, b.Shr(0xffffffff_u, b.Sub(64_u, subgroup_size)), gt);
-        auto* block = b.Block(Vector{
-            b.Decl(gt),
-            b.Assign(b.IndexAccessor(subgroup_size_mask, 0_u), lo),
-            b.Assign(b.IndexAccessor(subgroup_size_mask, 1_u), hi),
-        });
-        ctx.InsertFront(ep->body->statements, block);
-    }
-};
-
-SubgroupBallot::SubgroupBallot() = default;
-
-SubgroupBallot::~SubgroupBallot() = default;
-
-ast::transform::Transform::ApplyResult SubgroupBallot::Apply(const Program& src,
-                                                             const ast::transform::DataMap&,
-                                                             ast::transform::DataMap&) const {
-    return State(src).Run();
-}
-
-SubgroupBallot::SimdBallot::~SimdBallot() = default;
-
-const SubgroupBallot::SimdBallot* SubgroupBallot::SimdBallot::Clone(ast::CloneContext& ctx) const {
-    return ctx.dst->ASTNodes().Create<SubgroupBallot::SimdBallot>(ctx.dst->ID(),
-                                                                  ctx.dst->AllocateNodeID());
-}
-
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/ast_raise/subgroup_ballot.h b/src/tint/lang/msl/writer/ast_raise/subgroup_ballot.h
deleted file mode 100644
index 3d5922b..0000000
--- a/src/tint/lang/msl/writer/ast_raise/subgroup_ballot.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_MSL_WRITER_AST_RAISE_SUBGROUP_BALLOT_H_
-#define SRC_TINT_LANG_MSL_WRITER_AST_RAISE_SUBGROUP_BALLOT_H_
-
-#include <string>
-
-#include "src/tint/lang/wgsl/ast/internal_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::msl::writer {
-
-/// SubgroupBallot is a transform that replaces calls to `subgroupBallot()` with an
-/// implementation that uses MSL's `simd_ballot()`.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * CanonicalizeEntryPointIO
-class SubgroupBallot final : public Castable<SubgroupBallot, ast::transform::Transform> {
-  public:
-    /// Constructor
-    SubgroupBallot();
-
-    /// Destructor
-    ~SubgroupBallot() override;
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-
-    /// SimdBallot is an InternalAttribute that is used to decorate a stub function so
-    /// that the MSL backend transforms this into calls to the `simd_ballot` function.
-    class SimdBallot final : public Castable<SimdBallot, ast::InternalAttribute> {
-      public:
-        /// Constructor
-        /// @param pid the identifier of the program that owns this node
-        /// @param nid the unique node identifier
-        SimdBallot(GenerationID pid, ast::NodeID nid) : Base(pid, nid, Empty) {}
-
-        /// Destructor
-        ~SimdBallot() override;
-
-        /// @copydoc ast::InternalAttribute::InternalName
-        std::string InternalName() const override { return "simd_ballot"; }
-
-        /// Performs a deep clone of this object using the program::CloneContext `ctx`.
-        /// @param ctx the clone context
-        /// @return the newly cloned object
-        const SimdBallot* Clone(ast::CloneContext& ctx) const override;
-    };
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::msl::writer
-
-#endif  // SRC_TINT_LANG_MSL_WRITER_AST_RAISE_SUBGROUP_BALLOT_H_
diff --git a/src/tint/lang/msl/writer/ast_raise/subgroup_ballot_test.cc b/src/tint/lang/msl/writer/ast_raise/subgroup_ballot_test.cc
deleted file mode 100644
index d5d6ab5..0000000
--- a/src/tint/lang/msl/writer/ast_raise/subgroup_ballot_test.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/msl/writer/ast_raise/subgroup_ballot.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::msl::writer {
-namespace {
-
-using SubgroupBallotTest = ast::transform::TransformTest;
-
-TEST_F(SubgroupBallotTest, EmptyModule) {
-    auto* src = "";
-
-    EXPECT_FALSE(ShouldRun<SubgroupBallot>(src));
-}
-
-TEST_F(SubgroupBallotTest, DirectUse) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo() {
-  let pred = true;
-  let x : vec4u = subgroupBallot(pred);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(simd_ballot) @internal(disable_validation__function_has_no_body)
-fn tint_msl_simd_ballot(pred : bool) -> vec2<u32>
-
-var<private> tint_subgroup_size_mask : vec4<u32>;
-
-fn tint_msl_subgroup_ballot(pred : bool) -> vec4<u32> {
-  let tint_symbol = vec4<u32>(tint_msl_simd_ballot(pred), 0u, 0u);
-  return (tint_symbol & tint_subgroup_size_mask);
-}
-
-@compute @workgroup_size(64)
-fn foo(@builtin(subgroup_size) tint_subgroup_size : u32) {
-  {
-    let gt = (tint_subgroup_size > 32u);
-    tint_subgroup_size_mask[0u] = select((4294967295u >> (32u - tint_subgroup_size)), 4294967295u, gt);
-    tint_subgroup_size_mask[1u] = select(0u, (4294967295u >> (64u - tint_subgroup_size)), gt);
-  }
-  let pred = true;
-  let x : vec4u = tint_msl_subgroup_ballot(pred);
-}
-)";
-
-    auto got = Run<SubgroupBallot>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubgroupBallotTest, IndirectUse) {
-    auto* src = R"(
-enable subgroups;
-
-fn bar() -> vec4u {
-  let pred = true;
-  return subgroupBallot(pred);
-}
-
-@compute @workgroup_size(64)
-fn foo() {
-  let x: vec4u = bar();
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(simd_ballot) @internal(disable_validation__function_has_no_body)
-fn tint_msl_simd_ballot(pred : bool) -> vec2<u32>
-
-var<private> tint_subgroup_size_mask : vec4<u32>;
-
-fn tint_msl_subgroup_ballot(pred : bool) -> vec4<u32> {
-  let tint_symbol = vec4<u32>(tint_msl_simd_ballot(pred), 0u, 0u);
-  return (tint_symbol & tint_subgroup_size_mask);
-}
-
-fn bar() -> vec4u {
-  let pred = true;
-  return tint_msl_subgroup_ballot(pred);
-}
-
-@compute @workgroup_size(64)
-fn foo(@builtin(subgroup_size) tint_subgroup_size : u32) {
-  {
-    let gt = (tint_subgroup_size > 32u);
-    tint_subgroup_size_mask[0u] = select((4294967295u >> (32u - tint_subgroup_size)), 4294967295u, gt);
-    tint_subgroup_size_mask[1u] = select(0u, (4294967295u >> (64u - tint_subgroup_size)), gt);
-  }
-  let x : vec4u = bar();
-}
-)";
-
-    auto got = Run<SubgroupBallot>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubgroupBallotTest, PreexistingSubgroupSizeBuiltin) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn foo(@builtin(workgroup_id) group_id: vec3u,
-       @builtin(subgroup_size) size : u32,
-       @builtin(local_invocation_index) index : u32) {
-  let sz = size;
-  let pred = true;
-  let x : vec4u = subgroupBallot(pred);
-}
-)";
-
-    auto* expect =
-        R"(
-enable subgroups;
-
-@internal(simd_ballot) @internal(disable_validation__function_has_no_body)
-fn tint_msl_simd_ballot(pred : bool) -> vec2<u32>
-
-var<private> tint_subgroup_size_mask : vec4<u32>;
-
-fn tint_msl_subgroup_ballot(pred : bool) -> vec4<u32> {
-  let tint_symbol = vec4<u32>(tint_msl_simd_ballot(pred), 0u, 0u);
-  return (tint_symbol & tint_subgroup_size_mask);
-}
-
-@compute @workgroup_size(64)
-fn foo(@builtin(workgroup_id) group_id : vec3u, @builtin(subgroup_size) size : u32, @builtin(local_invocation_index) index : u32) {
-  {
-    let gt = (size > 32u);
-    tint_subgroup_size_mask[0u] = select((4294967295u >> (32u - size)), 4294967295u, gt);
-    tint_subgroup_size_mask[1u] = select(0u, (4294967295u >> (64u - size)), gt);
-  }
-  let sz = size;
-  let pred = true;
-  let x : vec4u = tint_msl_subgroup_ballot(pred);
-}
-)";
-
-    auto got = Run<SubgroupBallot>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/helpers/BUILD.bazel b/src/tint/lang/msl/writer/helpers/BUILD.bazel
index 2ef72a6..d56a19a 100644
--- a/src/tint/lang/msl/writer/helpers/BUILD.bazel
+++ b/src/tint/lang/msl/writer/helpers/BUILD.bazel
@@ -50,11 +50,6 @@
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/features",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/sem",
     "//src/tint/utils",
     "//src/tint/utils/containers",
     "//src/tint/utils/diagnostic",
diff --git a/src/tint/lang/msl/writer/helpers/BUILD.cmake b/src/tint/lang/msl/writer/helpers/BUILD.cmake
index d46b225..2b29562 100644
--- a/src/tint/lang/msl/writer/helpers/BUILD.cmake
+++ b/src/tint/lang/msl/writer/helpers/BUILD.cmake
@@ -51,11 +51,6 @@
   tint_lang_core_constant
   tint_lang_core_ir
   tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_features
-  tint_lang_wgsl_program
-  tint_lang_wgsl_sem
   tint_utils
   tint_utils_containers
   tint_utils_diagnostic
diff --git a/src/tint/lang/msl/writer/helpers/BUILD.gn b/src/tint/lang/msl/writer/helpers/BUILD.gn
index 569bb72..8b74a73 100644
--- a/src/tint/lang/msl/writer/helpers/BUILD.gn
+++ b/src/tint/lang/msl/writer/helpers/BUILD.gn
@@ -51,11 +51,6 @@
       "${tint_src_dir}/lang/core/constant",
       "${tint_src_dir}/lang/core/ir",
       "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/wgsl",
-      "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/features",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/sem",
       "${tint_src_dir}/utils",
       "${tint_src_dir}/utils/containers",
       "${tint_src_dir}/utils/diagnostic",
diff --git a/src/tint/lang/msl/writer/helpers/generate_bindings.cc b/src/tint/lang/msl/writer/helpers/generate_bindings.cc
index e0ca733..8f124ae 100644
--- a/src/tint/lang/msl/writer/helpers/generate_bindings.cc
+++ b/src/tint/lang/msl/writer/helpers/generate_bindings.cc
@@ -39,9 +39,6 @@
 #include "src/tint/lang/core/type/pointer.h"
 #include "src/tint/lang/core/type/storage_texture.h"
 #include "src/tint/lang/msl/writer/common/options.h"
-#include "src/tint/lang/wgsl/ast/module.h"
-#include "src/tint/lang/wgsl/program/program.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
 #include "src/tint/utils/ice/ice.h"
 #include "src/tint/utils/rtti/switch.h"
 
@@ -121,80 +118,4 @@
     return bindings;
 }
 
-Bindings GenerateBindings(const Program& program) {
-    // TODO(tint:1491): Use Inspector once we can get binding info for all
-    // variables, not just those referenced by entry points.
-
-    Bindings bindings{};
-
-    std::unordered_set<tint::BindingPoint> seen_binding_points;
-
-    // Collect next valid binding number per group
-    Hashmap<uint32_t, uint32_t, 4> group_to_next_binding_number;
-    Vector<tint::BindingPoint, 4> ext_tex_bps;
-    for (auto* var : program.AST().GlobalVariables()) {
-        if (auto* sem_var = program.Sem().Get(var)->As<sem::GlobalVariable>()) {
-            if (auto bp = sem_var->Attributes().binding_point) {
-                if (auto val = group_to_next_binding_number.Get(bp->group)) {
-                    *val = std::max(*val, bp->binding + 1);
-                } else {
-                    group_to_next_binding_number.Add(bp->group, bp->binding + 1);
-                }
-
-                // Store up the external textures, we'll add them in the next step
-                if (sem_var->Type()->UnwrapRef()->Is<core::type::ExternalTexture>()) {
-                    ext_tex_bps.Push(*bp);
-                    continue;
-                }
-
-                binding::BindingInfo info{bp->binding};
-                switch (sem_var->AddressSpace()) {
-                    case core::AddressSpace::kHandle:
-                        Switch(
-                            sem_var->Type()->UnwrapRef(),  //
-                            [&](const core::type::Sampler*) {
-                                bindings.sampler.emplace(*bp, info);
-                            },
-                            [&](const core::type::StorageTexture*) {
-                                bindings.storage_texture.emplace(*bp, info);
-                            },
-                            [&](const core::type::Texture*) {
-                                bindings.texture.emplace(*bp, info);
-                            });
-                        break;
-                    case core::AddressSpace::kStorage:
-                        bindings.storage.emplace(*bp, info);
-                        break;
-                    case core::AddressSpace::kUniform:
-                        bindings.uniform.emplace(*bp, info);
-                        break;
-
-                    case core::AddressSpace::kUndefined:
-                    case core::AddressSpace::kPixelLocal:
-                    case core::AddressSpace::kPrivate:
-                    case core::AddressSpace::kPushConstant:
-                    case core::AddressSpace::kIn:
-                    case core::AddressSpace::kOut:
-                    case core::AddressSpace::kFunction:
-                    case core::AddressSpace::kWorkgroup:
-                        break;
-                }
-            }
-        }
-    }
-
-    for (auto bp : ext_tex_bps) {
-        uint32_t g = bp.group;
-        uint32_t& next_num = group_to_next_binding_number.GetOrAddZero(g);
-
-        binding::BindingInfo plane0{bp.binding};
-        binding::BindingInfo plane1{next_num++};
-        binding::BindingInfo metadata{next_num++};
-
-        bindings.external_texture.emplace(bp, binding::ExternalTexture{metadata, plane0, plane1});
-    }
-
-    return bindings;
-}
-
 }  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/helpers/generate_bindings.h b/src/tint/lang/msl/writer/helpers/generate_bindings.h
index 273132b..cd67655 100644
--- a/src/tint/lang/msl/writer/helpers/generate_bindings.h
+++ b/src/tint/lang/msl/writer/helpers/generate_bindings.h
@@ -31,9 +31,6 @@
 #include "src/tint/lang/msl/writer/common/options.h"
 
 // Forward declarations
-namespace tint {
-class Program;
-}
 namespace tint::core::ir {
 class Module;
 }  // namespace tint::core::ir
@@ -45,11 +42,6 @@
 /// @returns the bindings
 Bindings GenerateBindings(const core::ir::Module& module);
 
-/// Generate the resource bindings
-/// @param program the program to generate from
-/// @returns the bindings
-Bindings GenerateBindings(const Program& program);
-
 }  // namespace tint::msl::writer
 
 #endif  // SRC_TINT_LANG_MSL_WRITER_HELPERS_GENERATE_BINDINGS_H_
diff --git a/src/tint/lang/msl/writer/writer.cc b/src/tint/lang/msl/writer/writer.cc
index 7820b84..df320c3 100644
--- a/src/tint/lang/msl/writer/writer.cc
+++ b/src/tint/lang/msl/writer/writer.cc
@@ -32,8 +32,10 @@
 
 #include "src/tint/lang/core/ir/module.h"
 #include "src/tint/lang/core/ir/var.h"
+#include "src/tint/lang/core/type/f16.h"
+#include "src/tint/lang/core/type/f32.h"
 #include "src/tint/lang/core/type/input_attachment.h"
-#include "src/tint/lang/msl/writer/ast_printer/ast_printer.h"
+#include "src/tint/lang/core/type/pointer.h"
 #include "src/tint/lang/msl/writer/common/option_helpers.h"
 #include "src/tint/lang/msl/writer/printer/printer.h"
 #include "src/tint/lang/msl/writer/raise/raise.h"
@@ -143,37 +145,4 @@
     return result;
 }
 
-Result<Output> Generate(const Program& program, const Options& options) {
-    if (!program.IsValid()) {
-        return Failure{program.Diagnostics()};
-    }
-
-    {
-        auto res = ValidateBindingOptions(options);
-        if (res != Success) {
-            return res.Failure();
-        }
-    }
-
-    Output output;
-
-    // Sanitize the program.
-    auto sanitized_result = Sanitize(program, options);
-    if (!sanitized_result.program.IsValid()) {
-        return Failure{sanitized_result.program.Diagnostics()};
-    }
-    output.needs_storage_buffer_sizes = sanitized_result.needs_storage_buffer_sizes;
-
-    // Generate the MSL code.
-    auto impl = std::make_unique<ASTPrinter>(sanitized_result.program, options);
-    if (!impl->Generate()) {
-        return Failure{impl->Diagnostics()};
-    }
-    output.msl = impl->Result();
-    output.has_invariant_attribute = impl->HasInvariant();
-    output.workgroup_info.allocations = impl->DynamicWorkgroupAllocations();
-
-    return output;
-}
-
 }  // namespace tint::msl::writer
diff --git a/src/tint/lang/msl/writer/writer.h b/src/tint/lang/msl/writer/writer.h
index 93f6505..020e11d 100644
--- a/src/tint/lang/msl/writer/writer.h
+++ b/src/tint/lang/msl/writer/writer.h
@@ -28,17 +28,12 @@
 #ifndef SRC_TINT_LANG_MSL_WRITER_WRITER_H_
 #define SRC_TINT_LANG_MSL_WRITER_WRITER_H_
 
-#include <string>
-
 #include "src/tint/lang/msl/writer/common/options.h"
 #include "src/tint/lang/msl/writer/common/output.h"
 #include "src/tint/utils/diagnostic/diagnostic.h"
 #include "src/tint/utils/result/result.h"
 
 // Forward declarations
-namespace tint {
-class Program;
-}  // namespace tint
 namespace tint::core::ir {
 class Module;
 }  // namespace tint::core::ir
@@ -58,13 +53,6 @@
 /// @returns the resulting MSL and supplementary information, or failure
 Result<Output> Generate(core::ir::Module& ir, const Options& options);
 
-/// Generate MSL for a program, according to a set of configuration options.
-/// The result will contain the MSL and supplementary information, or failure.
-/// @param program the program to translate to MSL
-/// @param options the configuration options to use when generating MSL
-/// @returns the resulting MSL and supplementary information, or failure
-Result<Output> Generate(const Program& program, const Options& options);
-
 }  // namespace tint::msl::writer
 
 #endif  // SRC_TINT_LANG_MSL_WRITER_WRITER_H_
diff --git a/src/tint/lang/msl/writer/writer_ast_fuzz.cc b/src/tint/lang/msl/writer/writer_ast_fuzz.cc
deleted file mode 100644
index 0441e5c..0000000
--- a/src/tint/lang/msl/writer/writer_ast_fuzz.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2024 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// GEN_BUILD:CONDITION(tint_build_wgsl_reader)
-
-#include <iostream>
-
-#include "src/tint/cmd/fuzz/wgsl/fuzz.h"
-#include "src/tint/lang/msl/writer/helpers/generate_bindings.h"
-#include "src/tint/lang/msl/writer/writer.h"
-#include "src/tint/lang/wgsl/ast/module.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-
-namespace tint::msl::writer {
-namespace {
-
-bool CanRun(const Program& program) {
-    if (program.AST().HasOverrides()) {
-        // MSL writer assumes the SubstituteOverride and SingleEntryPoint transforms have been run.
-        return false;
-    }
-
-    // Check for push constants, which the MSL writer does not support.
-    for (auto* global : program.AST().GlobalVariables()) {
-        if (program.Sem().Get(global)->AddressSpace() == core::AddressSpace::kPushConstant) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-void ASTFuzzer(const tint::Program& program, const fuzz::wgsl::Context& context, Options options) {
-    if (!CanRun(program)) {
-        return;
-    }
-
-    options.bindings = GenerateBindings(program);
-
-    auto res = tint::msl::writer::Generate(program, options);
-
-    if (res == Success && context.options.dump) {
-        std::cout << "Dumping generated MSL:\n" << res->msl << "\n";
-    }
-}
-
-}  // namespace
-}  // namespace tint::msl::writer
-
-TINT_WGSL_PROGRAM_FUZZER(tint::msl::writer::ASTFuzzer);
diff --git a/src/tint/lang/msl/writer/writer_bench.cc b/src/tint/lang/msl/writer/writer_bench.cc
index a48524f..fcf6866 100644
--- a/src/tint/lang/msl/writer/writer_bench.cc
+++ b/src/tint/lang/msl/writer/writer_bench.cc
@@ -70,7 +70,15 @@
                                                                           6);
     gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 7},
                                                                           7);
-    gen_options.bindings = tint::msl::writer::GenerateBindings(*program);
+    {
+        // Convert the AST program to an IR module, so that we can generating bindings data.
+        auto ir = tint::wgsl::reader::ProgramToLoweredIR(*program);
+        if (ir != Success) {
+            state.SkipWithError(ir.Failure().reason.Str());
+            return;
+        }
+        gen_options.bindings = tint::msl::writer::GenerateBindings(ir.Get());
+    }
 
     for (auto _ : state) {
         // Convert the AST program to an IR module.
@@ -87,50 +95,7 @@
     }
 }
 
-void GenerateMSL_AST(benchmark::State& state, std::string input_name) {
-    auto res = bench::GetWgslProgram(input_name);
-    if (res != Success) {
-        state.SkipWithError(res.Failure().reason.Str());
-        return;
-    }
-
-    // Remap resource numbers to a flat namespace.
-    const tint::Program* program = &res->program;
-    auto flattened = tint::wgsl::FlattenBindings(res->program);
-    if (flattened) {
-        program = &*flattened;
-    }
-
-    tint::msl::writer::Options gen_options = {};
-    gen_options.array_length_from_uniform.ubo_binding = 30;
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 0},
-                                                                          0);
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 1},
-                                                                          1);
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 2},
-                                                                          2);
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 3},
-                                                                          3);
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 4},
-                                                                          4);
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 5},
-                                                                          5);
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 6},
-                                                                          6);
-    gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(tint::BindingPoint{0, 7},
-                                                                          7);
-    gen_options.bindings = tint::msl::writer::GenerateBindings(*program);
-
-    for (auto _ : state) {
-        auto gen_res = Generate(*program, gen_options);
-        if (gen_res != Success) {
-            state.SkipWithError(gen_res.Failure().reason.Str());
-        }
-    }
-}
-
 TINT_BENCHMARK_PROGRAMS(GenerateMSL);
-TINT_BENCHMARK_PROGRAMS(GenerateMSL_AST);
 
 }  // namespace
 }  // namespace tint::msl::writer