Remove unused AST transforms.

Remove AST transforms which are no longer used.

Bug: 421916945
Change-Id: Ic62804a2eb68595f46eabcd20e280a8ceb8f0bf3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/248355
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.bazel b/src/tint/lang/wgsl/ast/transform/BUILD.bazel
index 31ae503..8ac475e 100644
--- a/src/tint/lang/wgsl/ast/transform/BUILD.bazel
+++ b/src/tint/lang/wgsl/ast/transform/BUILD.bazel
@@ -39,72 +39,23 @@
 cc_library(
   name = "transform",
   srcs = [
-    "add_empty_entry_point.cc",
-    "array_length_from_uniform.cc",
-    "binding_remapper.cc",
-    "builtin_polyfill.cc",
-    "canonicalize_entry_point_io.cc",
     "data.cc",
-    "demote_to_helper.cc",
-    "direct_variable_access.cc",
-    "disable_uniformity_analysis.cc",
-    "expand_compound_assignment.cc",
-    "first_index_offset.cc",
-    "fold_constants.cc",
-    "get_insertion_point.cc",
-    "hoist_to_decl_before.cc",
     "manager.cc",
-    "multiplanar_external_texture.cc",
-    "promote_initializers_to_let.cc",
-    "promote_side_effects_to_decl.cc",
-    "remove_continue_in_switch.cc",
-    "remove_phonies.cc",
-    "renamer.cc",
-    "robustness.cc",
     "simplify_pointers.cc",
-    "single_entry_point.cc",
-    "substitute_override.cc",
     "transform.cc",
     "unshadow.cc",
-    "vectorize_scalar_matrix_initializers.cc",
-    "zero_init_workgroup_memory.cc",
   ],
   hdrs = [
-    "add_empty_entry_point.h",
-    "array_length_from_uniform.h",
-    "binding_remapper.h",
-    "builtin_polyfill.h",
-    "canonicalize_entry_point_io.h",
     "data.h",
-    "demote_to_helper.h",
-    "direct_variable_access.h",
-    "disable_uniformity_analysis.h",
-    "expand_compound_assignment.h",
-    "first_index_offset.h",
-    "fold_constants.h",
-    "get_insertion_point.h",
-    "hoist_to_decl_before.h",
     "manager.h",
-    "multiplanar_external_texture.h",
-    "promote_initializers_to_let.h",
-    "promote_side_effects_to_decl.h",
-    "remove_continue_in_switch.h",
-    "remove_phonies.h",
-    "renamer.h",
-    "robustness.h",
     "simplify_pointers.h",
-    "single_entry_point.h",
-    "substitute_override.h",
     "transform.h",
     "unshadow.h",
-    "vectorize_scalar_matrix_initializers.h",
-    "zero_init_workgroup_memory.h",
   ],
   deps = [
     "//src/tint/api/common",
     "//src/tint/lang/core",
     "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/ir/transform",
     "//src/tint/lang/core/type",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
@@ -130,42 +81,17 @@
   name = "test",
   alwayslink = True,
   srcs = [
-    "add_empty_entry_point_test.cc",
-    "array_length_from_uniform_test.cc",
-    "binding_remapper_test.cc",
-    "builtin_polyfill_test.cc",
-    "canonicalize_entry_point_io_test.cc",
-    "demote_to_helper_test.cc",
-    "direct_variable_access_test.cc",
-    "disable_uniformity_analysis_test.cc",
-    "expand_compound_assignment_test.cc",
-    "first_index_offset_test.cc",
-    "fold_constants_test.cc",
-    "get_insertion_point_test.cc",
     "helper_test.h",
-    "hoist_to_decl_before_test.cc",
     "manager_test.cc",
-    "multiplanar_external_texture_test.cc",
-    "promote_initializers_to_let_test.cc",
-    "promote_side_effects_to_decl_test.cc",
-    "remove_continue_in_switch_test.cc",
-    "remove_phonies_test.cc",
-    "renamer_test.cc",
-    "robustness_test.cc",
     "simplify_pointers_test.cc",
-    "single_entry_point_test.cc",
-    "substitute_override_test.cc",
     "transform_test.cc",
     "unshadow_test.cc",
-    "vectorize_scalar_matrix_initializers_test.cc",
-    "zero_init_workgroup_memory_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/ir/transform",
     "//src/tint/lang/core/type",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.cmake b/src/tint/lang/wgsl/ast/transform/BUILD.cmake
index ff237f0..eb02cb9 100644
--- a/src/tint/lang/wgsl/ast/transform/BUILD.cmake
+++ b/src/tint/lang/wgsl/ast/transform/BUILD.cmake
@@ -39,71 +39,22 @@
 # Kind:      lib
 ################################################################################
 tint_add_target(tint_lang_wgsl_ast_transform lib
-  lang/wgsl/ast/transform/add_empty_entry_point.cc
-  lang/wgsl/ast/transform/add_empty_entry_point.h
-  lang/wgsl/ast/transform/array_length_from_uniform.cc
-  lang/wgsl/ast/transform/array_length_from_uniform.h
-  lang/wgsl/ast/transform/binding_remapper.cc
-  lang/wgsl/ast/transform/binding_remapper.h
-  lang/wgsl/ast/transform/builtin_polyfill.cc
-  lang/wgsl/ast/transform/builtin_polyfill.h
-  lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
-  lang/wgsl/ast/transform/canonicalize_entry_point_io.h
   lang/wgsl/ast/transform/data.cc
   lang/wgsl/ast/transform/data.h
-  lang/wgsl/ast/transform/demote_to_helper.cc
-  lang/wgsl/ast/transform/demote_to_helper.h
-  lang/wgsl/ast/transform/direct_variable_access.cc
-  lang/wgsl/ast/transform/direct_variable_access.h
-  lang/wgsl/ast/transform/disable_uniformity_analysis.cc
-  lang/wgsl/ast/transform/disable_uniformity_analysis.h
-  lang/wgsl/ast/transform/expand_compound_assignment.cc
-  lang/wgsl/ast/transform/expand_compound_assignment.h
-  lang/wgsl/ast/transform/first_index_offset.cc
-  lang/wgsl/ast/transform/first_index_offset.h
-  lang/wgsl/ast/transform/fold_constants.cc
-  lang/wgsl/ast/transform/fold_constants.h
-  lang/wgsl/ast/transform/get_insertion_point.cc
-  lang/wgsl/ast/transform/get_insertion_point.h
-  lang/wgsl/ast/transform/hoist_to_decl_before.cc
-  lang/wgsl/ast/transform/hoist_to_decl_before.h
   lang/wgsl/ast/transform/manager.cc
   lang/wgsl/ast/transform/manager.h
-  lang/wgsl/ast/transform/multiplanar_external_texture.cc
-  lang/wgsl/ast/transform/multiplanar_external_texture.h
-  lang/wgsl/ast/transform/promote_initializers_to_let.cc
-  lang/wgsl/ast/transform/promote_initializers_to_let.h
-  lang/wgsl/ast/transform/promote_side_effects_to_decl.cc
-  lang/wgsl/ast/transform/promote_side_effects_to_decl.h
-  lang/wgsl/ast/transform/remove_continue_in_switch.cc
-  lang/wgsl/ast/transform/remove_continue_in_switch.h
-  lang/wgsl/ast/transform/remove_phonies.cc
-  lang/wgsl/ast/transform/remove_phonies.h
-  lang/wgsl/ast/transform/renamer.cc
-  lang/wgsl/ast/transform/renamer.h
-  lang/wgsl/ast/transform/robustness.cc
-  lang/wgsl/ast/transform/robustness.h
   lang/wgsl/ast/transform/simplify_pointers.cc
   lang/wgsl/ast/transform/simplify_pointers.h
-  lang/wgsl/ast/transform/single_entry_point.cc
-  lang/wgsl/ast/transform/single_entry_point.h
-  lang/wgsl/ast/transform/substitute_override.cc
-  lang/wgsl/ast/transform/substitute_override.h
   lang/wgsl/ast/transform/transform.cc
   lang/wgsl/ast/transform/transform.h
   lang/wgsl/ast/transform/unshadow.cc
   lang/wgsl/ast/transform/unshadow.h
-  lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.cc
-  lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h
-  lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
-  lang/wgsl/ast/transform/zero_init_workgroup_memory.h
 )
 
 tint_target_add_dependencies(tint_lang_wgsl_ast_transform lib
   tint_api_common
   tint_lang_core
   tint_lang_core_constant
-  tint_lang_core_ir_transform
   tint_lang_core_type
   tint_lang_wgsl
   tint_lang_wgsl_ast
@@ -133,35 +84,11 @@
 # Condition: TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER
 ################################################################################
 tint_add_target(tint_lang_wgsl_ast_transform_test test
-  lang/wgsl/ast/transform/add_empty_entry_point_test.cc
-  lang/wgsl/ast/transform/array_length_from_uniform_test.cc
-  lang/wgsl/ast/transform/binding_remapper_test.cc
-  lang/wgsl/ast/transform/builtin_polyfill_test.cc
-  lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
-  lang/wgsl/ast/transform/demote_to_helper_test.cc
-  lang/wgsl/ast/transform/direct_variable_access_test.cc
-  lang/wgsl/ast/transform/disable_uniformity_analysis_test.cc
-  lang/wgsl/ast/transform/expand_compound_assignment_test.cc
-  lang/wgsl/ast/transform/first_index_offset_test.cc
-  lang/wgsl/ast/transform/fold_constants_test.cc
-  lang/wgsl/ast/transform/get_insertion_point_test.cc
   lang/wgsl/ast/transform/helper_test.h
-  lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
   lang/wgsl/ast/transform/manager_test.cc
-  lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
-  lang/wgsl/ast/transform/promote_initializers_to_let_test.cc
-  lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc
-  lang/wgsl/ast/transform/remove_continue_in_switch_test.cc
-  lang/wgsl/ast/transform/remove_phonies_test.cc
-  lang/wgsl/ast/transform/renamer_test.cc
-  lang/wgsl/ast/transform/robustness_test.cc
   lang/wgsl/ast/transform/simplify_pointers_test.cc
-  lang/wgsl/ast/transform/single_entry_point_test.cc
-  lang/wgsl/ast/transform/substitute_override_test.cc
   lang/wgsl/ast/transform/transform_test.cc
   lang/wgsl/ast/transform/unshadow_test.cc
-  lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_test.cc
-  lang/wgsl/ast/transform/zero_init_workgroup_memory_test.cc
 )
 
 tint_target_add_dependencies(tint_lang_wgsl_ast_transform_test test
@@ -169,7 +96,6 @@
   tint_lang_core
   tint_lang_core_constant
   tint_lang_core_ir
-  tint_lang_core_ir_transform
   tint_lang_core_type
   tint_lang_wgsl
   tint_lang_wgsl_ast
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.gn b/src/tint/lang/wgsl/ast/transform/BUILD.gn
index 8b06c3d..73306ea 100644
--- a/src/tint/lang/wgsl/ast/transform/BUILD.gn
+++ b/src/tint/lang/wgsl/ast/transform/BUILD.gn
@@ -45,71 +45,22 @@
 
 libtint_source_set("transform") {
   sources = [
-    "add_empty_entry_point.cc",
-    "add_empty_entry_point.h",
-    "array_length_from_uniform.cc",
-    "array_length_from_uniform.h",
-    "binding_remapper.cc",
-    "binding_remapper.h",
-    "builtin_polyfill.cc",
-    "builtin_polyfill.h",
-    "canonicalize_entry_point_io.cc",
-    "canonicalize_entry_point_io.h",
     "data.cc",
     "data.h",
-    "demote_to_helper.cc",
-    "demote_to_helper.h",
-    "direct_variable_access.cc",
-    "direct_variable_access.h",
-    "disable_uniformity_analysis.cc",
-    "disable_uniformity_analysis.h",
-    "expand_compound_assignment.cc",
-    "expand_compound_assignment.h",
-    "first_index_offset.cc",
-    "first_index_offset.h",
-    "fold_constants.cc",
-    "fold_constants.h",
-    "get_insertion_point.cc",
-    "get_insertion_point.h",
-    "hoist_to_decl_before.cc",
-    "hoist_to_decl_before.h",
     "manager.cc",
     "manager.h",
-    "multiplanar_external_texture.cc",
-    "multiplanar_external_texture.h",
-    "promote_initializers_to_let.cc",
-    "promote_initializers_to_let.h",
-    "promote_side_effects_to_decl.cc",
-    "promote_side_effects_to_decl.h",
-    "remove_continue_in_switch.cc",
-    "remove_continue_in_switch.h",
-    "remove_phonies.cc",
-    "remove_phonies.h",
-    "renamer.cc",
-    "renamer.h",
-    "robustness.cc",
-    "robustness.h",
     "simplify_pointers.cc",
     "simplify_pointers.h",
-    "single_entry_point.cc",
-    "single_entry_point.h",
-    "substitute_override.cc",
-    "substitute_override.h",
     "transform.cc",
     "transform.h",
     "unshadow.cc",
     "unshadow.h",
-    "vectorize_scalar_matrix_initializers.cc",
-    "vectorize_scalar_matrix_initializers.h",
-    "zero_init_workgroup_memory.cc",
-    "zero_init_workgroup_memory.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/ir/transform",
     "${tint_src_dir}/lang/core/type",
     "${tint_src_dir}/lang/wgsl",
     "${tint_src_dir}/lang/wgsl/ast",
@@ -132,35 +83,11 @@
   if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
     tint_unittests_source_set("unittests") {
       sources = [
-        "add_empty_entry_point_test.cc",
-        "array_length_from_uniform_test.cc",
-        "binding_remapper_test.cc",
-        "builtin_polyfill_test.cc",
-        "canonicalize_entry_point_io_test.cc",
-        "demote_to_helper_test.cc",
-        "direct_variable_access_test.cc",
-        "disable_uniformity_analysis_test.cc",
-        "expand_compound_assignment_test.cc",
-        "first_index_offset_test.cc",
-        "fold_constants_test.cc",
-        "get_insertion_point_test.cc",
         "helper_test.h",
-        "hoist_to_decl_before_test.cc",
         "manager_test.cc",
-        "multiplanar_external_texture_test.cc",
-        "promote_initializers_to_let_test.cc",
-        "promote_side_effects_to_decl_test.cc",
-        "remove_continue_in_switch_test.cc",
-        "remove_phonies_test.cc",
-        "renamer_test.cc",
-        "robustness_test.cc",
         "simplify_pointers_test.cc",
-        "single_entry_point_test.cc",
-        "substitute_override_test.cc",
         "transform_test.cc",
         "unshadow_test.cc",
-        "vectorize_scalar_matrix_initializers_test.cc",
-        "zero_init_workgroup_memory_test.cc",
       ]
       deps = [
         "${dawn_root}/src/utils:utils",
@@ -169,7 +96,6 @@
         "${tint_src_dir}/lang/core",
         "${tint_src_dir}/lang/core/constant",
         "${tint_src_dir}/lang/core/ir",
-        "${tint_src_dir}/lang/core/ir/transform",
         "${tint_src_dir}/lang/core/type",
         "${tint_src_dir}/lang/wgsl",
         "${tint_src_dir}/lang/wgsl/ast",
diff --git a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.cc b/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.cc
deleted file mode 100644
index ff63a55..0000000
--- a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.cc
+++ /dev/null
@@ -1,78 +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/wgsl/ast/transform/add_empty_entry_point.h"
-
-#include <utility>
-
-#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"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::AddEmptyEntryPoint);
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
-    for (auto* func : program.AST().Functions()) {
-        if (func->IsEntryPoint()) {
-            return false;
-        }
-    }
-    return true;
-}
-
-}  // namespace
-
-AddEmptyEntryPoint::AddEmptyEntryPoint() = default;
-
-AddEmptyEntryPoint::~AddEmptyEntryPoint() = default;
-
-Transform::ApplyResult AddEmptyEntryPoint::Apply(const Program& src,
-                                                 const DataMap&,
-                                                 DataMap&) const {
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    b.Func(b.Symbols().New("unused_entry_point"), {}, b.ty.void_(), {},
-           tint::Vector{
-               b.Stage(PipelineStage::kCompute),
-               b.WorkgroupSize(1_i),
-           });
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h b/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h
deleted file mode 100644
index 06d5de2..0000000
--- a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h
+++ /dev/null
@@ -1,51 +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_WGSL_AST_TRANSFORM_ADD_EMPTY_ENTRY_POINT_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_ADD_EMPTY_ENTRY_POINT_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Add an empty entry point to the module, if no other entry points exist.
-class AddEmptyEntryPoint final : public Castable<AddEmptyEntryPoint, Transform> {
-  public:
-    /// Constructor
-    AddEmptyEntryPoint();
-    /// Destructor
-    ~AddEmptyEntryPoint() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_ADD_EMPTY_ENTRY_POINT_H_
diff --git a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_test.cc b/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_test.cc
deleted file mode 100644
index fc1c704..0000000
--- a/src/tint/lang/wgsl/ast/transform/add_empty_entry_point_test.cc
+++ /dev/null
@@ -1,99 +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/wgsl/ast/transform/add_empty_entry_point.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using AddEmptyEntryPointTest = TransformTest;
-
-TEST_F(AddEmptyEntryPointTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_TRUE(ShouldRun<AddEmptyEntryPoint>(src));
-}
-
-TEST_F(AddEmptyEntryPointTest, ShouldRunExistingEntryPoint) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn existing() {}
-)";
-
-    EXPECT_FALSE(ShouldRun<AddEmptyEntryPoint>(src));
-}
-
-TEST_F(AddEmptyEntryPointTest, EmptyModule) {
-    auto* src = R"()";
-
-    auto* expect = R"(
-@compute @workgroup_size(1i)
-fn unused_entry_point() {
-}
-)";
-
-    auto got = Run<AddEmptyEntryPoint>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(AddEmptyEntryPointTest, ExistingEntryPoint) {
-    auto* src = R"(
-@fragment
-fn main() {
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<AddEmptyEntryPoint>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(AddEmptyEntryPointTest, NameClash) {
-    auto* src = R"(var<private> unused_entry_point : f32;)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1i)
-fn unused_entry_point_1() {
-}
-
-var<private> unused_entry_point : f32;
-)";
-
-    auto got = Run<AddEmptyEntryPoint>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.cc b/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.cc
deleted file mode 100644
index cea6a86..0000000
--- a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.cc
+++ /dev/null
@@ -1,343 +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/wgsl/ast/transform/array_length_from_uniform.h"
-
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/unary_op.h"
-#include "src/tint/lang/wgsl/ast/expression.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
-#include "src/tint/lang/wgsl/ast/unary_op_expression.h"
-#include "src/tint/lang/wgsl/ast/variable.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/array.h"
-#include "src/tint/lang/wgsl/sem/builtin_fn.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/expression.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/statement.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/unique_vector.h"
-#include "src/tint/utils/diagnostic/diagnostic.h"
-#include "src/tint/utils/ice/ice.h"
-#include "src/tint/utils/rtti/switch.h"
-#include "src/tint/utils/text/text_style.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ArrayLengthFromUniform);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ArrayLengthFromUniform::Config);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ArrayLengthFromUniform::Result);
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
-    for (auto* fn : program.AST().Functions()) {
-        if (auto* sem_fn = program.Sem().Get(fn)) {
-            for (auto* builtin : sem_fn->DirectlyCalledBuiltins()) {
-                if (builtin->Fn() == wgsl::BuiltinFn::kArrayLength) {
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
-ArrayLengthFromUniform::ArrayLengthFromUniform() = default;
-ArrayLengthFromUniform::~ArrayLengthFromUniform() = default;
-
-/// PIMPL state for the transform
-struct ArrayLengthFromUniform::State {
-    /// Constructor
-    /// @param program the source program
-    /// @param in the input transform data
-    /// @param out the output transform data
-    State(const Program& program, const DataMap& in, DataMap& out)
-        : src(program), outputs(out), cfg(in.Get<Config>()) {}
-
-    /// Runs the transform
-    /// @returns the new program or SkipTransform if the transform is not required
-    ApplyResult Run() {
-        if (cfg == nullptr) {
-            b.Diagnostics().AddError(Source{}) << "missing transform data for "
-                                               << tint::TypeInfo::Of<ArrayLengthFromUniform>().name;
-            return resolver::Resolve(b);
-        }
-
-        if (cfg->bindpoint_to_size_index.empty() || !ShouldRun(src)) {
-            return SkipTransform;
-        }
-
-        // Create the name of the array lengths uniform variable.
-        array_lengths_var = b.Symbols().New("tint_array_lengths");
-
-        // Replace all the arrayLength() calls.
-        for (auto* fn : src.AST().Functions()) {
-            if (auto* sem_fn = sem.Get(fn)) {
-                for (auto* call : sem_fn->DirectCalls()) {
-                    if (auto* target = call->Target()->As<sem::BuiltinFn>()) {
-                        if (target->Fn() == wgsl::BuiltinFn::kArrayLength) {
-                            ReplaceArrayLengthCall(call);
-                        }
-                    }
-                }
-            }
-        }
-
-        // Add the necessary array-length arguments to all the newly created array-length
-        // parameters.
-        while (!len_params_needing_args.IsEmpty()) {
-            AddArrayLengthArguments(len_params_needing_args.Pop());
-        }
-
-        // Add the tint_array_lengths module-scope uniform variable.
-        AddArrayLengthsUniformVar();
-
-        outputs.Add<Result>(used_size_indices);
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-  private:
-    // Replaces the arrayLength() builtin call with an array-length expression passed via a uniform
-    // buffer.
-    void ReplaceArrayLengthCall(const sem::Call* call) {
-        if (auto* replacement = ArrayLengthOf(call->Arguments()[0])) {
-            ctx.Replace(call->Declaration(), replacement);
-        }
-    }
-
-    /// @returns an AST expression that is equal to the arrayLength() of the runtime-sized array
-    /// accessed by the pointer expression @p expr, or nullptr on error or if the array is not in
-    /// the Config::bindpoint_to_size_index map.
-    const ast::Expression* ArrayLengthOf(const sem::Expression* expr) {
-        const ast::Expression* len = nullptr;
-        while (expr) {
-            expr = Switch(
-                expr,  //
-                [&](const sem::VariableUser* user) {
-                    len = ArrayLengthOf(user->Variable());
-                    return nullptr;
-                },
-                [&](const sem::MemberAccessorExpression* access) {
-                    return access->Object();  // Follow the object
-                },
-                [&](const sem::Expression* e) {
-                    return Switch(
-                        e->Declaration(),  //
-                        [&](const ast::UnaryOpExpression* unary) -> const sem::Expression* {
-                            switch (unary->op) {
-                                case core::UnaryOp::kAddressOf:
-                                case core::UnaryOp::kIndirection:
-                                    return sem.Get(unary->expr);  // Follow the object
-                                default:
-                                    TINT_ICE() << "unexpected unary op: " << unary->op;
-                            }
-                        },
-                        TINT_ICE_ON_NO_MATCH);
-                },
-                TINT_ICE_ON_NO_MATCH);
-        }
-        return len;
-    }
-
-    /// @returns an AST expression that is equal to the arrayLength() of the runtime-sized array
-    /// held by the module-scope variable or parameter @p var, or nullptr on error or if the array
-    /// is not in the Config::bindpoint_to_size_index map.
-    const ast::Expression* ArrayLengthOf(const sem::Variable* var) {
-        return Switch(
-            var,  //
-            [&](const sem::GlobalVariable* global) { return ArrayLengthOf(global); },
-            [&](const sem::Parameter* param) { return ArrayLengthOf(param); },
-            TINT_ICE_ON_NO_MATCH);
-    }
-
-    /// @returns an AST expression that is equal to the arrayLength() of the runtime-sized array
-    /// held by the module scope variable @p global, or nullptr on error or if the array is not in
-    /// the Config::bindpoint_to_size_index map.
-    const ast::Expression* ArrayLengthOf(const sem::GlobalVariable* global) {
-        auto binding = global->Attributes().binding_point;
-        TINT_ASSERT(binding);
-
-        auto idx_it = cfg->bindpoint_to_size_index.find(*binding);
-        if (idx_it == cfg->bindpoint_to_size_index.end()) {
-            // If the bindpoint_to_size_index map does not contain an entry for the storage buffer,
-            // then we preserve the arrayLength() call.
-            return nullptr;
-        }
-
-        uint32_t size_index = idx_it->second;
-        used_size_indices.insert(size_index);
-
-        // Load the total storage buffer size from the UBO.
-        uint32_t array_index = size_index / 4;
-        auto* vec_expr = b.IndexAccessor(
-            b.MemberAccessor(array_lengths_var, kArrayLengthsMemberName), u32(array_index));
-        uint32_t vec_index = size_index % 4;
-        auto* total_storage_buffer_size = b.IndexAccessor(vec_expr, u32(vec_index));
-
-        // Calculate actual array length
-        //                total_storage_buffer_size - array_offset
-        // array_length = ----------------------------------------
-        //                             array_stride
-        const Expression* total_size = total_storage_buffer_size;
-        if (DAWN_UNLIKELY(global->Type()->Is<core::type::Pointer>())) {
-            TINT_ICE() << "storage buffer variable should not be a pointer. "
-                          "These should have been removed by the SimplifyPointers transform";
-        }
-        auto* storage_buffer_type = global->Type()->UnwrapRef();
-        const core::type::Array* array_type = nullptr;
-        if (auto* str = storage_buffer_type->As<core::type::Struct>()) {
-            // The variable is a struct, so subtract the byte offset of the
-            // array member.
-            auto* array_member_sem = str->Members().Back();
-            array_type = array_member_sem->Type()->As<core::type::Array>();
-            total_size = b.Sub(total_storage_buffer_size, u32(array_member_sem->Offset()));
-        } else if (auto* arr = storage_buffer_type->As<core::type::Array>()) {
-            array_type = arr;
-        } else {
-            TINT_ICE() << "expected form of arrayLength argument to be &array_var or "
-                          "&struct_var.array_member";
-        }
-        return b.Div(total_size, u32(array_type->Stride()));
-    }
-
-    /// @returns an AST expression that is equal to the arrayLength() of the runtime-sized array
-    /// held by the object pointed to by the pointer parameter @p param.
-    const ast::Expression* ArrayLengthOf(const sem::Parameter* param) {
-        // Pointer originates from a parameter.
-        // Add a new array length parameter to the function, and use that.
-        auto len_name = param_lengths.GetOrAdd(param, [&] {
-            auto* fn = param->Owner()->As<sem::Function>();
-            auto name = b.Symbols().New(param->Declaration()->name->symbol.Name() + "_length");
-            auto* len_param = b.Param(name, b.ty.u32());
-            ctx.InsertAfter(fn->Declaration()->params, param->Declaration(), len_param);
-            len_params_needing_args.Add(param);
-            return name;
-        });
-        return b.Expr(len_name);
-    }
-
-    /// Constructs the uniform buffer variable that will hold the array lengths.
-    void AddArrayLengthsUniformVar() {
-        // Calculate the highest index in the array lengths array
-        uint32_t highest_index = 0;
-        for (auto idx : used_size_indices) {
-            if (idx > highest_index) {
-                highest_index = idx;
-            }
-        }
-
-        // Emit an array<vec4<u32>, N>, where N is 1/4 number of elements.
-        // We do this because UBOs require an element stride that is 16-byte aligned.
-        auto* buffer_size_struct =
-            b.Structure(b.Symbols().New("TintArrayLengths"),
-                        tint::Vector{
-                            b.Member(kArrayLengthsMemberName,
-                                     b.ty.array(b.ty.vec4<u32>(), u32((highest_index / 4) + 1))),
-                        });
-        b.GlobalVar(array_lengths_var, b.ty.Of(buffer_size_struct), core::AddressSpace::kUniform,
-                    b.Group(AInt(cfg->ubo_binding.group)),
-                    b.Binding(AInt(cfg->ubo_binding.binding)));
-    }
-
-    /// Adds an additional array-length argument to all the calls to the function that owns the
-    /// pointer parameter @p param. This may add new entries to #len_params_needing_args.
-    void AddArrayLengthArguments(const sem::Parameter* param) {
-        auto* fn = param->Owner()->As<sem::Function>();
-        for (auto* call : fn->CallSites()) {
-            auto* arg = call->Arguments()[param->Index()];
-            if (auto* len = ArrayLengthOf(arg); len) {
-                ctx.InsertAfter(call->Declaration()->args, arg->Declaration(), len);
-            } else {
-                // Callee expects an array length, but there's no binding for it.
-                // Call arrayLength() at the call-site.
-                len = b.Call(wgsl::BuiltinFn::kArrayLength, ctx.Clone(arg->Declaration()));
-                ctx.InsertAfter(call->Declaration()->args, arg->Declaration(), len);
-            }
-        }
-    }
-
-    /// Name of the array-lengths struct member that holds all the array lengths.
-    static constexpr std::string_view kArrayLengthsMemberName = "array_lengths";
-
-    /// The source program
-    const Program& src;
-    /// The transform outputs
-    DataMap& outputs;
-    /// The transform config
-    const Config* const cfg;
-    /// The target program builder
-    ProgramBuilder b;
-    /// The clone context
-    program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
-    /// Alias to src.Sem()
-    const sem::Info& sem = src.Sem();
-    /// Name of the uniform buffer variable that holds the array lengths
-    Symbol array_lengths_var;
-    /// A map of pointer-parameter to the name of the new array-length parameter.
-    Hashmap<const sem::Parameter*, Symbol, 8> param_lengths;
-    /// Indices into the uniform buffer array indices that are statically used.
-    std::unordered_set<uint32_t> used_size_indices;
-    /// A vector of array-length parameters which need corresponding array-length arguments for all
-    /// callsites.
-    UniqueVector<const sem::Parameter*, 8> len_params_needing_args;
-};
-
-Transform::ApplyResult ArrayLengthFromUniform::Apply(const Program& src,
-                                                     const DataMap& inputs,
-                                                     DataMap& outputs) const {
-    return State{src, inputs, outputs}.Run();
-}
-
-ArrayLengthFromUniform::Config::Config() = default;
-ArrayLengthFromUniform::Config::Config(BindingPoint ubo_bp) : ubo_binding(ubo_bp) {}
-ArrayLengthFromUniform::Config::Config(const Config&) = default;
-ArrayLengthFromUniform::Config& ArrayLengthFromUniform::Config::operator=(const Config&) = default;
-ArrayLengthFromUniform::Config::~Config() = default;
-
-ArrayLengthFromUniform::Result::Result(std::unordered_set<uint32_t> used_size_indices_in)
-    : used_size_indices(std::move(used_size_indices_in)) {}
-ArrayLengthFromUniform::Result::Result(const Result&) = default;
-ArrayLengthFromUniform::Result::~Result() = default;
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h b/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h
deleted file mode 100644
index b8ae52c..0000000
--- a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h
+++ /dev/null
@@ -1,129 +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_WGSL_AST_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// ArrayLengthFromUniform is a transform that implements calls to arrayLength()
-/// by calculating the length from the total size of the storage buffer, which
-/// is received via a uniform buffer.
-///
-/// The generated uniform buffer will have the form:
-/// ```
-/// struct buffer_size_struct {
-///  buffer_size : array<u32, 8>;
-/// };
-///
-/// @group(0) @binding(30)
-/// var<uniform> buffer_size_ubo : buffer_size_struct;
-/// ```
-/// The binding group and number used for this uniform buffer is provided via
-/// the `Config` transform input. The `Config` struct also defines the mapping
-/// from a storage buffer's `BindingPoint` to the array index that will be used
-/// to get the size of that buffer.
-///
-/// This transform assumes that the `SimplifyPointers`
-/// transforms have been run before it so that arguments to the arrayLength
-/// builtin always have the form `&resource.array`.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * SimplifyPointers
-class ArrayLengthFromUniform final : public Castable<ArrayLengthFromUniform, Transform> {
-  public:
-    /// Constructor
-    ArrayLengthFromUniform();
-    /// Destructor
-    ~ArrayLengthFromUniform() override;
-
-    /// Configuration options for the ArrayLengthFromUniform transform.
-    struct Config final : public Castable<Config, Data> {
-        /// Constructor
-        Config();
-
-        /// Constructor
-        /// @param ubo_bp the binding point to use for the generated uniform buffer.
-        explicit Config(BindingPoint ubo_bp);
-
-        /// Copy constructor
-        Config(const Config&);
-
-        /// Copy assignment
-        /// @return this Config
-        Config& operator=(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// The binding point to use for the generated uniform buffer.
-        BindingPoint ubo_binding;
-
-        /// The mapping from binding point to the index for the buffer size lookup.
-        std::unordered_map<BindingPoint, uint32_t> bindpoint_to_size_index;
-
-        /// Reflection for this class
-        TINT_REFLECT(Config, ubo_binding, bindpoint_to_size_index);
-    };
-
-    /// Information produced about what the transform did.
-    /// If there were no calls to the arrayLength() builtin, then no Result will
-    /// be emitted.
-    struct Result final : public Castable<Result, Data> {
-        /// Constructor
-        /// @param used_size_indices Indices into the UBO that are statically used.
-        explicit Result(std::unordered_set<uint32_t> used_size_indices);
-
-        /// Copy constructor
-        Result(const Result&);
-
-        /// Destructor
-        ~Result() override;
-
-        /// Indices into the UBO that are statically used.
-        std::unordered_set<uint32_t> used_size_indices;
-    };
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_
diff --git a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_test.cc b/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_test.cc
deleted file mode 100644
index 47aedb5..0000000
--- a/src/tint/lang/wgsl/ast/transform/array_length_from_uniform_test.cc
+++ /dev/null
@@ -1,709 +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/wgsl/ast/transform/array_length_from_uniform.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
-#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using ArrayLengthFromUniformTest = TransformTest;
-
-TEST_F(ArrayLengthFromUniformTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    EXPECT_FALSE(ShouldRun<ArrayLengthFromUniform>(src, data));
-}
-
-TEST_F(ArrayLengthFromUniformTest, ShouldRunNoArrayLength) {
-    auto* src = R"(
-struct SB {
-  x : i32,
-  arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    EXPECT_FALSE(ShouldRun<ArrayLengthFromUniform>(src, data));
-}
-
-TEST_F(ArrayLengthFromUniformTest, ShouldRunWithArrayLength) {
-    auto* src = R"(
-struct SB {
-  x : i32,
-  arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = arrayLength(&sb.arr);
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    EXPECT_TRUE(ShouldRun<ArrayLengthFromUniform>(src, data));
-}
-
-TEST_F(ArrayLengthFromUniformTest, Error_MissingTransformData) {
-    auto* src = R"(
-struct SB {
-  x : i32,
-  arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = arrayLength(&sb.arr);
-}
-)";
-
-    auto* expect = "error: missing transform data for tint::ast::transform::ArrayLengthFromUniform";
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ArrayLengthFromUniformTest, Basic) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read> sb : array<i32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = arrayLength(&sb);
-}
-)";
-
-    auto* expect = R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-@group(0) @binding(0) var<storage, read> sb : array<i32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = (tint_array_lengths.array_lengths[0u][0u] / 4u);
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<ArrayLengthFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(std::unordered_set<uint32_t>({0}), val->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, BasicInStruct) {
-    auto* src = R"(
-struct SB {
-  x : i32,
-  arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = arrayLength(&sb.arr);
-}
-)";
-
-    auto* expect = R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-    EXPECT_EQ(std::unordered_set<uint32_t>({0}),
-              got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-// Should output the same as BasicInStruct because SimplifyPointers outputs the same AST for
-// explicit and implicit pointer dereference.
-TEST_F(ArrayLengthFromUniformTest, BasicInStruct_ViaPointerDot) {
-    auto* src = R"(
-struct SB {
-  x : i32,
-  arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  let p = &sb;
-  var len : u32 = arrayLength(&p.arr);
-}
-)";
-
-    auto* expect = R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-    EXPECT_EQ(std::unordered_set<uint32_t>({0}),
-              got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, MultipleStorageBuffers) {
-    auto* src = R"(
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-};
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-};
-struct SB4 {
-  x : i32,
-  arr4 : array<vec4<f32>>,
-};
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-@group(2) @binding(2) var<storage, read> sb3 : array<vec4<f32>>;
-@group(3) @binding(2) var<storage, read> sb4 : SB4;
-@group(4) @binding(2) var<storage, read> sb5 : array<vec4<f32>>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len1 : u32 = arrayLength(&(sb1.arr1));
-  var len2 : u32 = arrayLength(&(sb2.arr2));
-  var len3 : u32 = arrayLength(&sb3);
-  var len4 : u32 = arrayLength(&(sb4.arr4));
-  var len5 : u32 = arrayLength(&sb5);
-  var x : u32 = (len1 + len2 + len3 + len4 + len5);
-}
-)";
-
-    auto* expect = R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 2u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-}
-
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-}
-
-struct SB4 {
-  x : i32,
-  arr4 : array<vec4<f32>>,
-}
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-
-@group(2) @binding(2) var<storage, read> sb3 : array<vec4<f32>>;
-
-@group(3) @binding(2) var<storage, read> sb4 : SB4;
-
-@group(4) @binding(2) var<storage, read> sb5 : array<vec4<f32>>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len1 : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-  var len2 : u32 = ((tint_array_lengths.array_lengths[0u][1u] - 16u) / 16u);
-  var len3 : u32 = (tint_array_lengths.array_lengths[0u][2u] / 16u);
-  var len4 : u32 = ((tint_array_lengths.array_lengths[0u][3u] - 16u) / 16u);
-  var len5 : u32 = (tint_array_lengths.array_lengths[1u][0u] / 16u);
-  var x : u32 = ((((len1 + len2) + len3) + len4) + len5);
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 2}, 0);
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{1u, 2}, 1);
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{2u, 2}, 2);
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{3u, 2}, 3);
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{4u, 2}, 4);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-    EXPECT_EQ(std::unordered_set<uint32_t>({0, 1, 2, 3, 4}),
-              got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, MultipleUnusedStorageBuffers) {
-    auto* src = R"(
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-};
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-};
-struct SB4 {
-  x : i32,
-  arr4 : array<vec4<f32>>,
-};
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-@group(2) @binding(2) var<storage, read> sb3 : array<vec4<f32>>;
-@group(3) @binding(2) var<storage, read> sb4 : SB4;
-@group(4) @binding(2) var<storage, read> sb5 : array<vec4<f32>>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len1 : u32 = arrayLength(&(sb1.arr1));
-  var len3 : u32 = arrayLength(&sb3);
-  var x : u32 = (len1 + len3);
-}
-)";
-
-    auto* expect = R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-}
-
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-}
-
-struct SB4 {
-  x : i32,
-  arr4 : array<vec4<f32>>,
-}
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-
-@group(2) @binding(2) var<storage, read> sb3 : array<vec4<f32>>;
-
-@group(3) @binding(2) var<storage, read> sb4 : SB4;
-
-@group(4) @binding(2) var<storage, read> sb5 : array<vec4<f32>>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len1 : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-  var len3 : u32 = (tint_array_lengths.array_lengths[0u][2u] / 16u);
-  var x : u32 = (len1 + len3);
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 2}, 0);
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{1u, 2}, 1);
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{2u, 2}, 2);
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{3u, 2}, 3);
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{4u, 2}, 4);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-    EXPECT_EQ(std::unordered_set<uint32_t>({0, 2}),
-              got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, NoArrayLengthCalls) {
-    auto* src = R"(
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = &(sb.arr);
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(src, str(got));
-    EXPECT_EQ(got.data.Get<ArrayLengthFromUniform::Result>(), nullptr);
-}
-
-TEST_F(ArrayLengthFromUniformTest, MissingBindingPointToIndexMapping) {
-    auto* src = R"(
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-};
-
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-};
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len1 : u32 = arrayLength(&(sb1.arr1));
-  var len2 : u32 = arrayLength(&(sb2.arr2));
-  var x : u32 = (len1 + len2);
-}
-)";
-
-    auto* expect =
-        R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-}
-
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-}
-
-@group(0) @binding(2) var<storage, read> sb1 : SB1;
-
-@group(1) @binding(2) var<storage, read> sb2 : SB2;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len1 : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-  var len2 : u32 = arrayLength(&(sb2.arr2));
-  var x : u32 = (len1 + len2);
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 2}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-    EXPECT_EQ(std::unordered_set<uint32_t>({0}),
-              got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = arrayLength(&sb.arr);
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-};
-)";
-
-    auto* expect = R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = ((tint_array_lengths.array_lengths[0u][0u] - 4u) / 4u);
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 0);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-    EXPECT_EQ(std::unordered_set<uint32_t>({0}),
-              got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, PtrParam_SingleUse) {
-    auto* src = R"(
-@binding(0) @group(0) var<storage, read_write> arr : array<u32>;
-
-fn f2(p : ptr<storage, array<u32>, read_write>) -> u32 {
-  return arrayLength(p);
-}
-
-fn f1(p : ptr<storage, array<u32>, read_write>) -> u32 {
-  return f2(p);
-}
-
-fn f0(p : ptr<storage, array<u32>, read_write>) -> u32 {
-  return f1(p);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  arr[0] = f0(&arr);
-}
-)";
-
-    auto* expect =
-        R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 1u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-@binding(0) @group(0) var<storage, read_write> arr : array<u32>;
-
-fn f2(p : ptr<storage, array<u32>, read_write>, p_length : u32) -> u32 {
-  return p_length;
-}
-
-fn f1(p : ptr<storage, array<u32>, read_write>, p_length_1 : u32) -> u32 {
-  return f2(p, p_length_1);
-}
-
-fn f0(p : ptr<storage, array<u32>, read_write>, p_length_2 : u32) -> u32 {
-  return f1(p, p_length_2);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  arr[0] = f0(&(arr), (tint_array_lengths.array_lengths[0u][3u] / 4u));
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{0, 0}, 3);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-    EXPECT_EQ(std::unordered_set<uint32_t>({3}),
-              got.data.Get<ArrayLengthFromUniform::Result>()->used_size_indices);
-}
-
-TEST_F(ArrayLengthFromUniformTest, MissingBindingPoint_PtrParam_MultipleUse) {
-    auto* src = R"(
-@binding(0) @group(0) var<storage, read_write> arr_a : array<u32>;
-@binding(0) @group(1) var<storage, read_write> arr_b : array<u32>;
-
-fn f2(p2 : ptr<storage, array<u32>, read_write>) -> u32 {
-  return arrayLength(p2);
-}
-
-fn f1(p1 : ptr<storage, array<u32>, read_write>) -> u32 {
-  return f2(p1) + arrayLength(p1);
-}
-
-fn f0(p0 : ptr<storage, array<u32>, read_write>) -> u32 {
-  return f1(p0) + arrayLength(p0);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  arr_a[0] = f0(&arr_a) + arrayLength(&arr_a);
-  arr_b[0] = f0(&arr_b) + arrayLength(&arr_b);
-}
-)";
-
-    auto* expect = R"(
-struct TintArrayLengths {
-  array_lengths : array<vec4<u32>, 2u>,
-}
-
-@group(0) @binding(30) var<uniform> tint_array_lengths : TintArrayLengths;
-
-@binding(0) @group(0) var<storage, read_write> arr_a : array<u32>;
-
-@binding(0) @group(1) var<storage, read_write> arr_b : array<u32>;
-
-fn f2(p2 : ptr<storage, array<u32>, read_write>, p2_length : u32) -> u32 {
-  return p2_length;
-}
-
-fn f1(p1 : ptr<storage, array<u32>, read_write>, p1_length : u32) -> u32 {
-  return (f2(p1, p1_length) + p1_length);
-}
-
-fn f0(p0 : ptr<storage, array<u32>, read_write>, p0_length : u32) -> u32 {
-  return (f1(p0, p0_length) + p0_length);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  arr_a[0] = (f0(&(arr_a), arrayLength(&(arr_a))) + arrayLength(&(arr_a)));
-  arr_b[0] = (f0(&(arr_b), (tint_array_lengths.array_lengths[1u][1u] / 4u)) + (tint_array_lengths.array_lengths[1u][1u] / 4u));
-}
-)";
-
-    ArrayLengthFromUniform::Config cfg({0, 30u});
-    cfg.bindpoint_to_size_index.emplace(BindingPoint{1, 0}, 5);
-
-    DataMap data;
-    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
-
-    auto got = Run<Unshadow, SimplifyPointers, ArrayLengthFromUniform>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/binding_remapper.cc b/src/tint/lang/wgsl/ast/transform/binding_remapper.cc
deleted file mode 100644
index 96316fd..0000000
--- a/src/tint/lang/wgsl/ast/transform/binding_remapper.cc
+++ /dev/null
@@ -1,146 +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/wgsl/ast/transform/binding_remapper.h"
-
-#include <string>
-#include <unordered_set>
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#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/function.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/text/string.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::BindingRemapper);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::BindingRemapper::Remappings);
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::ast::transform {
-
-BindingRemapper::Remappings::Remappings() = default;
-BindingRemapper::Remappings::Remappings(BindingPoints bp, AccessControls ac, bool may_collide)
-    : binding_points(std::move(bp)),
-      access_controls(std::move(ac)),
-      allow_collisions(may_collide) {}
-
-BindingRemapper::Remappings::Remappings(const Remappings&) = default;
-BindingRemapper::Remappings::~Remappings() = default;
-
-BindingRemapper::BindingRemapper() = default;
-BindingRemapper::~BindingRemapper() = default;
-
-Transform::ApplyResult BindingRemapper::Apply(const Program& src,
-                                              const DataMap& inputs,
-                                              DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    auto* remappings = inputs.Get<Remappings>();
-    if (!remappings) {
-        b.Diagnostics().AddError(Source{}) << "missing transform data for " << TypeInfo().name;
-        return resolver::Resolve(b);
-    }
-
-    if (!remappings->allow_collisions && remappings->binding_points.empty() &&
-        remappings->access_controls.empty()) {
-        return SkipTransform;
-    }
-
-    for (auto* var : src.AST().Globals<Var>()) {
-        if (var->HasBindingPoint()) {
-            auto* global_sem = src.Sem().Get<sem::GlobalVariable>(var);
-
-            // The original binding point
-            BindingPoint from = *global_sem->Attributes().binding_point;
-
-            // The binding point after remapping
-            BindingPoint bp = from;
-
-            // Replace any group or binding attributes.
-            // Note: This has to be performed *before* remapping access controls, as
-            // `ctx.Clone(var->attributes)` depend on these replacements.
-            auto bp_it = remappings->binding_points.find(from);
-            if (bp_it != remappings->binding_points.end()) {
-                BindingPoint to = bp_it->second;
-                auto* new_group = b.Group(AInt(to.group));
-                auto* new_binding = b.Binding(AInt(to.binding));
-
-                auto* old_group = GetAttribute<GroupAttribute>(var->attributes);
-                auto* old_binding = GetAttribute<BindingAttribute>(var->attributes);
-
-                ctx.Replace(old_group, new_group);
-                ctx.Replace(old_binding, new_binding);
-                bp = to;
-            }
-            // Add `DisableValidationAttribute`s if required
-            if (remappings->allow_collisions) {
-                auto* attribute = b.Disable(DisabledValidation::kBindingPointCollision);
-                ctx.InsertBefore(var->attributes, *var->attributes.begin(), attribute);
-            }
-
-            // Replace any access controls.
-            auto ac_it = remappings->access_controls.find(from);
-            if (ac_it != remappings->access_controls.end()) {
-                core::Access access = ac_it->second;
-                if (access == core::Access::kUndefined) {
-                    b.Diagnostics().AddError(Source{})
-                        << "invalid access mode (" << static_cast<uint32_t>(access) << ")";
-                    return resolver::Resolve(b);
-                }
-                auto* sem = src.Sem().Get(var);
-                if (sem->AddressSpace() != core::AddressSpace::kStorage) {
-                    b.Diagnostics().AddError(Source{})
-                        << "cannot apply access control to variable with address space "
-                        << sem->AddressSpace();
-                    return resolver::Resolve(b);
-                }
-                auto* ty = sem->Type()->UnwrapRef();
-                auto inner_ty = CreateASTTypeFor(ctx, ty);
-                auto* new_var =
-                    b.create<Var>(ctx.Clone(var->source),                  // source
-                                  b.Ident(ctx.Clone(var->name->symbol)),   // name
-                                  inner_ty,                                // type
-                                  ctx.Clone(var->declared_address_space),  // address space
-                                  b.Expr(access),                          // access
-                                  ctx.Clone(var->initializer),             // initializer
-                                  ctx.Clone(var->attributes));             // attributes
-                ctx.Replace(var, new_var);
-            }
-        }
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/binding_remapper.h b/src/tint/lang/wgsl/ast/transform/binding_remapper.h
deleted file mode 100644
index 2378011..0000000
--- a/src/tint/lang/wgsl/ast/transform/binding_remapper.h
+++ /dev/null
@@ -1,98 +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_WGSL_AST_TRANSFORM_BINDING_REMAPPER_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_BINDING_REMAPPER_H_
-
-#include <unordered_map>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/core/access.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// BindingPoint is an alias to BindingPoint
-using BindingPoint = BindingPoint;
-
-/// BindingRemapper is a transform used to remap resource binding points and
-/// access controls.
-class BindingRemapper final : public Castable<BindingRemapper, Transform> {
-  public:
-    /// BindingPoints is a map of old binding point to new binding point
-    using BindingPoints = std::unordered_map<BindingPoint, BindingPoint>;
-
-    /// AccessControls is a map of old binding point to new access control
-    using AccessControls = std::unordered_map<BindingPoint, core::Access>;
-
-    /// Remappings is consumed by the BindingRemapper transform.
-    /// Data holds information about shader usage and constant buffer offsets.
-    struct Remappings final : public Castable<Remappings, Data> {
-        /// Constructor
-        Remappings();
-
-        /// Constructor
-        /// @param bp a map of new binding points
-        /// @param ac a map of new access controls
-        /// @param may_collide If true, then validation will be disabled for
-        /// binding point collisions generated by this transform
-        Remappings(BindingPoints bp, AccessControls ac, bool may_collide = true);
-
-        /// Copy constructor
-        Remappings(const Remappings&);
-
-        /// Destructor
-        ~Remappings() override;
-
-        /// A map of old binding point to new binding point
-        BindingPoints binding_points;
-
-        /// A map of old binding point to new access controls
-        AccessControls access_controls;
-
-        /// If true, then validation will be disabled for binding point collisions
-        /// generated by this transform
-        bool allow_collisions = false;
-
-        /// Reflection for this class
-        TINT_REFLECT(Remappings, binding_points, access_controls, allow_collisions);
-    };
-
-    /// Constructor
-    BindingRemapper();
-    ~BindingRemapper() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_BINDING_REMAPPER_H_
diff --git a/src/tint/lang/wgsl/ast/transform/binding_remapper_test.cc b/src/tint/lang/wgsl/ast/transform/binding_remapper_test.cc
deleted file mode 100644
index 30697f0..0000000
--- a/src/tint/lang/wgsl/ast/transform/binding_remapper_test.cc
+++ /dev/null
@@ -1,419 +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/wgsl/ast/transform/binding_remapper.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using BindingRemapperTest = TransformTest;
-
-TEST_F(BindingRemapperTest, ShouldRunEmptyRemappings) {
-    auto* src = R"()";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
-                                          BindingRemapper::AccessControls{},
-                                          /* allow collisions */ false);
-
-    EXPECT_FALSE(ShouldRun<BindingRemapper>(src, data));
-}
-
-TEST_F(BindingRemapperTest, ShouldRunEmptyRemappingsWithCollisions) {
-    auto* src = R"()";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
-                                          BindingRemapper::AccessControls{},
-                                          /* allow collisions */ true);
-
-    EXPECT_TRUE(ShouldRun<BindingRemapper>(src, data));
-}
-
-TEST_F(BindingRemapperTest, ShouldRunBindingPointRemappings) {
-    auto* src = R"()";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(
-        BindingRemapper::BindingPoints{
-            {{2, 1}, {1, 2}},
-        },
-        BindingRemapper::AccessControls{});
-
-    EXPECT_TRUE(ShouldRun<BindingRemapper>(src, data));
-}
-
-TEST_F(BindingRemapperTest, ShouldRunAccessControlRemappings) {
-    auto* src = R"()";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
-                                          BindingRemapper::AccessControls{
-                                              {{2, 1}, core::Access::kWrite},
-                                          });
-
-    EXPECT_TRUE(ShouldRun<BindingRemapper>(src, data));
-}
-
-TEST_F(BindingRemapperTest, NoRemappingsWithoutCollisions) {
-    auto* src = R"(
-struct S {
-  a : f32,
-}
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    auto* expect = src;
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
-                                          BindingRemapper::AccessControls{},
-                                          /* allow_collisions */ false);
-    auto got = Run<BindingRemapper>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, NoRemappingsWithCollisions) {
-    auto* src = R"(
-struct S {
-  a : f32,
-}
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : f32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(2) @binding(1) var<storage, read> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(BindingRemapper::BindingPoints{},
-                                          BindingRemapper::AccessControls{},
-                                          /* allow_collisions */ true);
-    auto got = Run<BindingRemapper>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, RemapBindingPoints) {
-    auto* src = R"(
-struct S {
-  a : f32,
-};
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : f32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(2) var<storage, read> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(
-        BindingRemapper::BindingPoints{
-            {{2, 1}, {1, 2}},  // Remap
-            {{4, 5}, {6, 7}},  // Not found
-                               // Keep @group(3) @binding(2) as is
-        },
-        BindingRemapper::AccessControls{});
-    auto got = Run<BindingRemapper>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, RemapAccessControls) {
-    auto* src = R"(
-struct S {
-  a : f32,
-};
-
-@group(2) @binding(1) var<storage, read_write> a : S;
-
-@group(3) @binding(2) var<storage, read_write> b : S;
-
-@group(4) @binding(3) var<storage, read> c : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : f32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(2) @binding(1) var<storage, read_write> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(3) @binding(2) var<storage, read_write> b : S;
-
-@internal(disable_validation__binding_point_collision) @group(4) @binding(3) var<storage, read> c : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(
-        BindingRemapper::BindingPoints{},
-        BindingRemapper::AccessControls{
-            {{2, 1}, core::Access::kReadWrite},  // Modify access control
-            // Keep @group(3) @binding(2) as is
-            {{4, 3}, core::Access::kRead},  // Add access control
-        });
-    auto got = Run<BindingRemapper>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, RemapAll) {
-    auto* src = R"(
-struct S {
-  a : f32,
-};
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : f32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(4) @binding(5) var<storage, read_write> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(6) @binding(7) var<storage, read_write> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(
-        BindingRemapper::BindingPoints{
-            {{2, 1}, {4, 5}},
-            {{3, 2}, {6, 7}},
-        },
-        BindingRemapper::AccessControls{
-            {{2, 1}, core::Access::kReadWrite},
-            {{3, 2}, core::Access::kReadWrite},
-        });
-    auto got = Run<BindingRemapper>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, BindingCollisionsSameEntryPoint) {
-    auto* src = R"(
-struct S {
-  i : i32,
-};
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@group(4) @binding(3) var<storage, read> c : S;
-
-@group(5) @binding(4) var<storage, read> d : S;
-
-@compute @workgroup_size(1)
-fn f() {
-  let x : i32 = (((a.i + b.i) + c.i) + d.i);
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  i : i32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(1) var<storage, read> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(1) var<storage, read> b : S;
-
-@internal(disable_validation__binding_point_collision) @group(5) @binding(4) var<storage, read> c : S;
-
-@internal(disable_validation__binding_point_collision) @group(5) @binding(4) var<storage, read> d : S;
-
-@compute @workgroup_size(1)
-fn f() {
-  let x : i32 = (((a.i + b.i) + c.i) + d.i);
-}
-)";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(
-        BindingRemapper::BindingPoints{
-            {{2, 1}, {1, 1}},
-            {{3, 2}, {1, 1}},
-            {{4, 3}, {5, 4}},
-        },
-        BindingRemapper::AccessControls{}, true);
-    auto got = Run<BindingRemapper>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, BindingCollisionsDifferentEntryPoints) {
-    auto* src = R"(
-struct S {
-  i : i32,
-};
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@group(4) @binding(3) var<storage, read> c : S;
-
-@group(5) @binding(4) var<storage, read> d : S;
-
-@compute @workgroup_size(1)
-fn f1() {
-  let x : i32 = (a.i + c.i);
-}
-
-@compute @workgroup_size(1)
-fn f2() {
-  let x : i32 = (b.i + d.i);
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  i : i32,
-}
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(1) var<storage, read> a : S;
-
-@internal(disable_validation__binding_point_collision) @group(1) @binding(1) var<storage, read> b : S;
-
-@internal(disable_validation__binding_point_collision) @group(5) @binding(4) var<storage, read> c : S;
-
-@internal(disable_validation__binding_point_collision) @group(5) @binding(4) var<storage, read> d : S;
-
-@compute @workgroup_size(1)
-fn f1() {
-  let x : i32 = (a.i + c.i);
-}
-
-@compute @workgroup_size(1)
-fn f2() {
-  let x : i32 = (b.i + d.i);
-}
-)";
-
-    DataMap data;
-    data.Add<BindingRemapper::Remappings>(
-        BindingRemapper::BindingPoints{
-            {{2, 1}, {1, 1}},
-            {{3, 2}, {1, 1}},
-            {{4, 3}, {5, 4}},
-        },
-        BindingRemapper::AccessControls{}, true);
-    auto got = Run<BindingRemapper>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BindingRemapperTest, NoData) {
-    auto* src = R"(
-struct S {
-  a : f32,
-}
-
-@group(2) @binding(1) var<storage, read> a : S;
-
-@group(3) @binding(2) var<storage, read> b : S;
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-
-    auto* expect = R"(error: missing transform data for tint::ast::transform::BindingRemapper)";
-
-    auto got = Run<BindingRemapper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
deleted file mode 100644
index 1b57794..0000000
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.cc
+++ /dev/null
@@ -1,1617 +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/wgsl/ast/transform/builtin_polyfill.h"
-
-#include <algorithm>
-#include <cstdint>
-#include <tuple>
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/lang/core/type/storage_texture.h"
-#include "src/tint/lang/core/type/texture_dimension.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/builtin_fn.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/type_expression.h"
-#include "src/tint/lang/wgsl/sem/value_conversion.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/rtti/switch.h"
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::BuiltinPolyfill);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::BuiltinPolyfill::Config);
-
-namespace tint::ast::transform {
-
-/// BinaryOpSignature is tuple of a binary op, LHS type and RHS type
-using BinaryOpSignature =
-    std::tuple<core::BinaryOp, const core::type::Type*, const core::type::Type*>;
-
-/// PIMPL state for the transform
-struct BuiltinPolyfill::State {
-    /// 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
-    Transform::ApplyResult Run() {
-        for (auto* node : src.ASTNodes().Objects()) {
-            Switch(
-                node,  //
-                [&](const CallExpression* expr) { Call(expr); },
-                [&](const BinaryExpression* bin_op) {
-                    auto* s = src.Sem().Get(bin_op);
-                    if (!s || s->Stage() != core::EvaluationStage::kRuntime) {
-                        return;  // Only polyfill runtime expressions
-                    }
-                    switch (bin_op->op) {
-                        case core::BinaryOp::kShiftLeft:
-                        case core::BinaryOp::kShiftRight: {
-                            if (cfg.builtins.bitshift_modulo) {
-                                ctx.Replace(bin_op,
-                                            [this, bin_op] { return BitshiftModulo(bin_op); });
-                                made_changes = true;
-                            }
-                            break;
-                        }
-                        case core::BinaryOp::kDivide: {
-                            if (cfg.builtins.int_div_mod) {
-                                auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
-                                if (lhs_ty->IsIntegerScalarOrVector()) {
-                                    ctx.Replace(bin_op,
-                                                [this, bin_op] { return IntDivMod(bin_op); });
-                                    made_changes = true;
-                                }
-                            }
-                            break;
-                        }
-                        case core::BinaryOp::kModulo: {
-                            if (cfg.builtins.int_div_mod) {
-                                auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
-                                if (lhs_ty->IsIntegerScalarOrVector()) {
-                                    ctx.Replace(bin_op,
-                                                [this, bin_op] { return IntDivMod(bin_op); });
-                                    made_changes = true;
-                                }
-                            }
-                            if (cfg.builtins.precise_float_mod) {
-                                auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
-                                if (lhs_ty->IsFloatScalarOrVector()) {
-                                    ctx.Replace(bin_op,
-                                                [this, bin_op] { return PreciseFloatMod(bin_op); });
-                                    made_changes = true;
-                                }
-                            }
-                            break;
-                        }
-                        default:
-                            break;
-                    }
-                },
-                [&](const Expression* expr) {
-                    if (cfg.builtins.bgra8unorm) {
-                        if (auto* ty_expr = src.Sem().Get<sem::TypeExpression>(expr)) {
-                            if (auto* tex = ty_expr->Type()->As<core::type::StorageTexture>()) {
-                                if (tex->TexelFormat() == core::TexelFormat::kBgra8Unorm) {
-                                    ctx.Replace(expr, [this, tex] {
-                                        return ctx.dst->Expr(ctx.dst->ty.storage_texture(
-                                            tex->Dim(), core::TexelFormat::kRgba8Unorm,
-                                            tex->Access()));
-                                    });
-                                    made_changes = true;
-                                }
-                            }
-                        }
-                    }
-                });
-        }
-
-        if (!made_changes) {
-            return SkipTransform;
-        }
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-  private:
-    /// The source program
-    const Program& src;
-    /// The transform config
-    const Config& cfg;
-    /// The destination program builder
-    ProgramBuilder b;
-    /// The clone context
-    program::CloneContext ctx{&b, &src};
-    /// The source clone context
-    const sem::Info& sem = src.Sem();
-    /// Polyfill functions for binary operators.
-    Hashmap<BinaryOpSignature, Symbol, 8> binary_op_polyfills;
-    /// Polyfill builtins.
-    Hashmap<const sem::BuiltinFn*, Symbol, 8> builtin_polyfills;
-    /// Polyfill f32 conversion to i32 or u32 (or vectors of)
-    Hashmap<const core::type::Type*, Symbol, 2> f32_conv_polyfills;
-    /// True if the transform has made changes (i.e. the program needs cloning)
-    bool made_changes = false;
-
-    ////////////////////////////////////////////////////////////////////////////
-    // Function polyfills
-    ////////////////////////////////////////////////////////////////////////////
-
-    /// Builds the polyfill function for the `acosh` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol acosh(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_acosh");
-        uint32_t width = WidthOf(ty);
-
-        auto V = [&](AFloat value) -> const Expression* {
-            const Expression* expr = b.Expr(value);
-            if (width == 1) {
-                return expr;
-            }
-            return b.Call(T(ty), expr);
-        };
-
-        tint::Vector<const Statement*, 4> body;
-        switch (cfg.builtins.acosh) {
-            case Level::kFull:
-                // return log(x + sqrt(x*x - 1));
-                body.Push(b.Return(
-                    b.Call("log", b.Add("x", b.Call("sqrt", b.Sub(b.Mul("x", "x"), 1_a))))));
-                break;
-            case Level::kRangeCheck: {
-                // return select(acosh(x), 0, x < 1);
-                body.Push(b.Return(
-                    b.Call("select", b.Call("acosh", "x"), V(0.0_a), b.LessThan("x", V(1.0_a)))));
-                break;
-            }
-            default:
-                TINT_ICE() << "unhandled polyfill level: " << static_cast<int>(cfg.builtins.acosh);
-        }
-
-        b.Func(name, tint::Vector{b.Param("x", T(ty))}, T(ty), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `asinh` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol asinh(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_sinh");
-
-        // return log(x + sqrt(x*x + 1));
-        b.Func(name, tint::Vector{b.Param("x", T(ty))}, T(ty),
-               tint::Vector{
-                   b.Return(b.Call("log", b.Add("x", b.Call("sqrt", b.Add(b.Mul("x", "x"), 1_a))))),
-               });
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `atanh` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol atanh(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_atanh");
-        uint32_t width = WidthOf(ty);
-
-        auto V = [&](AFloat value) -> const Expression* {
-            const Expression* expr = b.Expr(value);
-            if (width == 1) {
-                return expr;
-            }
-            return b.Call(T(ty), expr);
-        };
-
-        tint::Vector<const Statement*, 1> body;
-        switch (cfg.builtins.atanh) {
-            case Level::kFull:
-                // return log((1+x) / (1-x)) * 0.5
-                body.Push(
-                    b.Return(b.Mul(b.Call("log", b.Div(b.Add(1_a, "x"), b.Sub(1_a, "x"))), 0.5_a)));
-                break;
-            case Level::kRangeCheck:
-                // return select(atanh(x), 0, x >= 1);
-                body.Push(b.Return(b.Call("select", b.Call("atanh", "x"), V(0.0_a),
-                                          b.GreaterThanEqual("x", V(1.0_a)))));
-                break;
-            default:
-                TINT_ICE() << "unhandled polyfill level: " << static_cast<int>(cfg.builtins.acosh);
-        }
-
-        b.Func(name, tint::Vector{b.Param("x", T(ty))}, T(ty), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `clamp` builtin when called with integer arguments
-    /// (scalar or vector)
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol clampInteger(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_clamp");
-
-        b.Func(name,
-               tint::Vector{
-                   b.Param("e", T(ty)),
-                   b.Param("low", T(ty)),
-                   b.Param("high", T(ty)),
-               },
-               T(ty),
-               tint::Vector{
-                   // return min(max(e, low), high);
-                   b.Return(b.Call("min", b.Call("max", "e", "low"), "high")),
-               });
-        return name;
-    }
-
-    /// Builds the polyfill function for the `countLeadingZeros` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol countLeadingZeros(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_count_leading_zeros");
-        uint32_t width = WidthOf(ty);
-
-        // Returns either u32 or vecN<u32>
-        auto U = [&] {
-            if (width == 1) {
-                return b.ty.u32();
-            }
-            return b.ty.vec<u32>(width);
-        };
-        auto V = [&](uint32_t value) -> const Expression* {
-            return ScalarOrVector(width, u32(value));
-        };
-        b.Func(
-            name,
-            tint::Vector{
-                b.Param("v", T(ty)),
-            },
-            T(ty),
-            tint::Vector{
-                // var x = U(v);
-                b.Decl(b.Var("x", b.Call(U(), b.Expr("v")))),
-                // let b16 = select(0, 16, x <= 0x0000ffff);
-                b.Decl(b.Let("b16",
-                             b.Call("select", V(0), V(16), b.LessThanEqual("x", V(0x0000ffff))))),
-                // x = x << b16;
-                b.Assign("x", b.Shl("x", "b16")),
-                // let b8  = select(0, 8,  x <= 0x00ffffff);
-                b.Decl(
-                    b.Let("b8", b.Call("select", V(0), V(8), b.LessThanEqual("x", V(0x00ffffff))))),
-                // x = x << b8;
-                b.Assign("x", b.Shl("x", "b8")),
-                // let b4  = select(0, 4,  x <= 0x0fffffff);
-                b.Decl(
-                    b.Let("b4", b.Call("select", V(0), V(4), b.LessThanEqual("x", V(0x0fffffff))))),
-                // x = x << b4;
-                b.Assign("x", b.Shl("x", "b4")),
-                // let b2  = select(0, 2,  x <= 0x3fffffff);
-                b.Decl(
-                    b.Let("b2", b.Call("select", V(0), V(2), b.LessThanEqual("x", V(0x3fffffff))))),
-                // x = x << b2;
-                b.Assign("x", b.Shl("x", "b2")),
-                // let b1  = select(0, 1,  x <= 0x7fffffff);
-                b.Decl(
-                    b.Let("b1", b.Call("select", V(0), V(1), b.LessThanEqual("x", V(0x7fffffff))))),
-                // let is_zero  = select(0, 1, x == 0);
-                b.Decl(b.Let("is_zero", b.Call("select", V(0), V(1), b.Equal("x", V(0))))),
-                // return R((b16 | b8 | b4 | b2 | b1) + zero);
-                b.Return(b.Call(T(ty), b.Add(b.Or(b.Or(b.Or(b.Or("b16", "b8"), "b4"), "b2"), "b1"),
-                                             "is_zero"))),
-            });
-        return name;
-    }
-
-    /// Builds the polyfill function for the `countTrailingZeros` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol countTrailingZeros(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_count_trailing_zeros");
-        uint32_t width = WidthOf(ty);
-
-        // Returns either u32 or vecN<u32>
-        auto U = [&] {
-            if (width == 1) {
-                return b.ty.u32();
-            }
-            return b.ty.vec<u32>(width);
-        };
-        auto V = [&](uint32_t value) -> const Expression* {
-            return ScalarOrVector(width, u32(value));
-        };
-        auto B = [&](const Expression* value) -> const Expression* {
-            if (width == 1) {
-                return b.Call<bool>(value);
-            }
-            return b.Call(b.ty.vec<bool>(width), value);
-        };
-        b.Func(
-            name,
-            tint::Vector{
-                b.Param("v", T(ty)),
-            },
-            T(ty),
-            tint::Vector{
-                // var x = U(v);
-                b.Decl(b.Var("x", b.Call(U(), b.Expr("v")))),
-                // let b16 = select(16, 0, bool(x & 0x0000ffff));
-                b.Decl(b.Let("b16", b.Call("select", V(16), V(0), B(b.And("x", V(0x0000ffff)))))),
-                // x = x >> b16;
-                b.Assign("x", b.Shr("x", "b16")),
-                // let b8  = select(8,  0, bool(x & 0x000000ff));
-                b.Decl(b.Let("b8", b.Call("select", V(8), V(0), B(b.And("x", V(0x000000ff)))))),
-                // x = x >> b8;
-                b.Assign("x", b.Shr("x", "b8")),
-                // let b4  = select(4,  0, bool(x & 0x0000000f));
-                b.Decl(b.Let("b4", b.Call("select", V(4), V(0), B(b.And("x", V(0x0000000f)))))),
-                // x = x >> b4;
-                b.Assign("x", b.Shr("x", "b4")),
-                // let b2  = select(2,  0, bool(x & 0x00000003));
-                b.Decl(b.Let("b2", b.Call("select", V(2), V(0), B(b.And("x", V(0x00000003)))))),
-                // x = x >> b2;
-                b.Assign("x", b.Shr("x", "b2")),
-                // let b1  = select(1,  0, bool(x & 0x00000001));
-                b.Decl(b.Let("b1", b.Call("select", V(1), V(0), B(b.And("x", V(0x00000001)))))),
-                // let is_zero  = select(0, 1, x == 0);
-                b.Decl(b.Let("is_zero", b.Call("select", V(0), V(1), b.Equal("x", V(0))))),
-                // return R((b16 | b8 | b4 | b2 | b1) + zero);
-                b.Return(b.Call(T(ty), b.Add(b.Or(b.Or(b.Or(b.Or("b16", "b8"), "b4"), "b2"), "b1"),
-                                             "is_zero"))),
-            });
-        return name;
-    }
-
-    /// Builds the polyfill function for the `extractBits` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol extractBits(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_extract_bits");
-        uint32_t width = WidthOf(ty);
-
-        constexpr uint32_t W = 32u;  // 32-bit
-
-        auto vecN_u32 = [&](const Expression* value) -> const Expression* {
-            if (width == 1) {
-                return value;
-            }
-            return b.Call(b.ty.vec<u32>(width), value);
-        };
-
-        tint::Vector<const Statement*, 8> body{
-            b.Decl(b.Let("s", b.Call("min", "offset", u32(W)))),
-            b.Decl(b.Let("e", b.Call("min", u32(W), b.Add("s", "count")))),
-        };
-
-        switch (cfg.builtins.extract_bits) {
-            case Level::kFull:
-                body.Push(b.Decl(b.Let("shl", b.Sub(u32(W), "e"))));
-                body.Push(b.Decl(b.Let("shr", b.Add("shl", "s"))));
-                // Here we don't want the shl and shr modulos the rhs, so handle the `rhs >= 32u`
-                // cases using `select`. In order to handle the signed shr `lhs >> rhs` corrently,
-                // use `(lhs >> 31u) >> 1u` if `rhs >= 32u`.
-                body.Push(b.Decl(b.Let("shl_result", b.Call("select", b.Call(T(ty)),
-                                                            b.Shl("v", vecN_u32(b.Expr("shl"))),
-                                                            b.LessThan("shl", 32_u)))));
-                body.Push(b.Return(b.Call(
-                    "select",
-                    b.Shr(b.Shr("shl_result", vecN_u32(b.Expr(31_u))), vecN_u32(b.Expr(1_u))),
-                    b.Shr("shl_result", vecN_u32(b.Expr("shr"))), b.LessThan("shr", 32_u))
-
-                                       ));
-                break;
-            case Level::kClampParameters:
-                body.Push(b.Return(b.Call("extractBits", "v", "s", b.Sub("e", "s"))));
-                break;
-            default:
-                TINT_ICE() << "unhandled polyfill level: "
-                           << static_cast<int>(cfg.builtins.extract_bits);
-        }
-
-        b.Func(name,
-               tint::Vector{
-                   b.Param("v", T(ty)),
-                   b.Param("offset", b.ty.u32()),
-                   b.Param("count", b.ty.u32()),
-               },
-               T(ty), std::move(body));
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `firstLeadingBit` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol firstLeadingBit(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_first_leading_bit");
-        uint32_t width = WidthOf(ty);
-
-        // Returns either u32 or vecN<u32>
-        auto U = [&] {
-            if (width == 1) {
-                return b.ty.u32();
-            }
-            return b.ty.vec<u32>(width);
-        };
-        auto V = [&](uint32_t value) -> const Expression* {
-            return ScalarOrVector(width, u32(value));
-        };
-        auto B = [&](const Expression* value) -> const Expression* {
-            if (width == 1) {
-                return b.Call<bool>(value);
-            }
-            return b.Call(b.ty.vec<bool>(width), value);
-        };
-
-        const Expression* x = nullptr;
-        if (ty->IsUnsignedIntegerScalarOrVector()) {
-            x = b.Expr("v");
-        } else {
-            // If ty is signed, then the value is inverted if the sign is negative
-            x = b.Call("select",                        //
-                       b.Call(U(), "v"),                //
-                       b.Call(U(), b.Complement("v")),  //
-                       b.LessThan("v", ScalarOrVector(width, 0_i)));
-        }
-
-        b.Func(
-            name,
-            tint::Vector{
-                b.Param("v", T(ty)),
-            },
-            T(ty),
-            tint::Vector{
-                // var x = v;                          (unsigned)
-                // var x = select(U(v), ~U(v), v < 0); (signed)
-                b.Decl(b.Var("x", x)),
-                // let b16 = select(0, 16, bool(x & 0xffff0000));
-                b.Decl(b.Let("b16", b.Call("select", V(0), V(16), B(b.And("x", V(0xffff0000)))))),
-                // x = x >> b16;
-                b.Assign("x", b.Shr("x", "b16")),
-                // let b8  = select(0, 8,  bool(x & 0x0000ff00));
-                b.Decl(b.Let("b8", b.Call("select", V(0), V(8), B(b.And("x", V(0x0000ff00)))))),
-                // x = x >> b8;
-                b.Assign("x", b.Shr("x", "b8")),
-                // let b4  = select(0, 4,  bool(x & 0x000000f0));
-                b.Decl(b.Let("b4", b.Call("select", V(0), V(4), B(b.And("x", V(0x000000f0)))))),
-                // x = x >> b4;
-                b.Assign("x", b.Shr("x", "b4")),
-                // let b2  = select(0, 2,  bool(x & 0x0000000c));
-                b.Decl(b.Let("b2", b.Call("select", V(0), V(2), B(b.And("x", V(0x0000000c)))))),
-                // x = x >> b2;
-                b.Assign("x", b.Shr("x", "b2")),
-                // let b1  = select(0, 1,  bool(x & 0x00000002));
-                b.Decl(b.Let("b1", b.Call("select", V(0), V(1), B(b.And("x", V(0x00000002)))))),
-                // let is_zero  = select(0, 0xffffffff, x == 0);
-                b.Decl(b.Let("is_zero", b.Call("select", V(0), V(0xffffffff), b.Equal("x", V(0))))),
-                // return R(b16 | b8 | b4 | b2 | b1 | zero);
-                b.Return(b.Call(
-                    T(ty), b.Or(b.Or(b.Or(b.Or(b.Or("b16", "b8"), "b4"), "b2"), "b1"), "is_zero"))),
-            });
-        return name;
-    }
-
-    /// Builds the polyfill function for the `firstTrailingBit` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol firstTrailingBit(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_first_trailing_bit");
-        uint32_t width = WidthOf(ty);
-
-        // Returns either u32 or vecN<u32>
-        auto U = [&] {
-            if (width == 1) {
-                return b.ty.u32();
-            }
-            return b.ty.vec<u32>(width);
-        };
-        auto V = [&](uint32_t value) -> const Expression* {
-            return ScalarOrVector(width, u32(value));
-        };
-        auto B = [&](const Expression* value) -> const Expression* {
-            if (width == 1) {
-                return b.Call<bool>(value);
-            }
-            return b.Call(b.ty.vec<bool>(width), value);
-        };
-        b.Func(
-            name,
-            tint::Vector{
-                b.Param("v", T(ty)),
-            },
-            T(ty),
-            tint::Vector{
-                // var x = U(v);
-                b.Decl(b.Var("x", b.Call(U(), b.Expr("v")))),
-                // let b16 = select(16, 0, bool(x & 0x0000ffff));
-                b.Decl(b.Let("b16", b.Call("select", V(16), V(0), B(b.And("x", V(0x0000ffff)))))),
-                // x = x >> b16;
-                b.Assign("x", b.Shr("x", "b16")),
-                // let b8  = select(8,  0, bool(x & 0x000000ff));
-                b.Decl(b.Let("b8", b.Call("select", V(8), V(0), B(b.And("x", V(0x000000ff)))))),
-                // x = x >> b8;
-                b.Assign("x", b.Shr("x", "b8")),
-                // let b4  = select(4,  0, bool(x & 0x0000000f));
-                b.Decl(b.Let("b4", b.Call("select", V(4), V(0), B(b.And("x", V(0x0000000f)))))),
-                // x = x >> b4;
-                b.Assign("x", b.Shr("x", "b4")),
-                // let b2  = select(2,  0, bool(x & 0x00000003));
-                b.Decl(b.Let("b2", b.Call("select", V(2), V(0), B(b.And("x", V(0x00000003)))))),
-                // x = x >> b2;
-                b.Assign("x", b.Shr("x", "b2")),
-                // let b1  = select(1,  0, bool(x & 0x00000001));
-                b.Decl(b.Let("b1", b.Call("select", V(1), V(0), B(b.And("x", V(0x00000001)))))),
-                // let is_zero  = select(0, 0xffffffff, x == 0);
-                b.Decl(b.Let("is_zero", b.Call("select", V(0), V(0xffffffff), b.Equal("x", V(0))))),
-                // return R(b16 | b8 | b4 | b2 | b1 | is_zero);
-                b.Return(b.Call(
-                    T(ty), b.Or(b.Or(b.Or(b.Or(b.Or("b16", "b8"), "b4"), "b2"), "b1"), "is_zero"))),
-            });
-        return name;
-    }
-
-    /// Builds the polyfill function for the `fwidthFine` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol fwidthFine(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_fwidth_fine");
-        // WGSL polyfill function:
-        //      fn tint_fwidth_fine(v : T) -> T {
-        //          return abs(dpdxFine(v)) + abs(dpdyFine(v));
-        //      }
-        auto body = tint::Vector{
-            b.Return(b.Add(b.Call("abs", b.Call("dpdxFine", "v")),
-                           b.Call("abs", b.Call("dpdyFine", "v")))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("v", T(ty)),
-               },
-               T(ty), body);
-        return name;
-    }
-
-    /// Builds the polyfill function for the `insertBits` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol insertBits(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_insert_bits");
-        uint32_t width = WidthOf(ty);
-
-        // Currently in WGSL parameters of insertBits must be i32, u32, vecN<i32> or vecN<u32>
-        if (DAWN_UNLIKELY(((!ty->DeepestElement()->IsAnyOf<core::type::I32, core::type::U32>())))) {
-            TINT_ICE()
-                << "insertBits polyfill only support i32, u32, and vector of i32 or u32, got "
-                << ty->FriendlyName();
-        }
-
-        constexpr uint32_t W = 32u;  // 32-bit
-
-        auto V = [&](auto value) -> const Expression* {
-            const Expression* expr = b.Expr(value);
-            if (!ty->IsUnsignedIntegerScalarOrVector()) {
-                expr = b.Call<i32>(expr);
-            }
-            if (ty->Is<core::type::Vector>()) {
-                expr = b.Call(T(ty), expr);
-            }
-            return expr;
-        };
-        auto U = [&](auto value) -> const Expression* {
-            if (width == 1) {
-                return b.Expr(value);
-            }
-            return b.vec(b.ty.u32(), width, value);
-        };
-
-        // Polyfill algorithm:
-        //      s = min(offset, 32u);
-        //      e = min(32u, (s + count));
-        //      mask = (((1u << s) - 1u) ^ ((1u << e) - 1u));
-        //      return (((n << s) & mask) | (v & ~(mask)));
-        // Note that the algorithm above use the left-shifting in C++ manner, but in WGSL, HLSL, MSL
-        // the rhs are modulo to bit-width of lhs (that is 32u in this case), and in GLSL the result
-        // is undefined if rhs is greater than or equal to bit-width of lhs. The results of `x << y`
-        // in C++ and HLSL are different when `y >= 32u`, and the `s` and `e` defined above can be
-        // 32u, which are cases we must handle specially. Replace all `(x << y)` to
-        // `select(Tx(), x << y, y < 32u)`, in which `Tx` is the type of x, where y can be greater
-        // than or equal to 32u.
-        // WGSL polyfill function:
-        //      fn tint_insert_bits(v : T, n : T, offset : u32, count : u32) -> T {
-        //          let e = offset + count;
-        //          let mask = (
-        //                        (select(0u, 1u << offset, offset < 32u) - 1u) ^
-        //                        (select(0u, 1u << e, e < 32u) - 1u)
-        //                     );
-        //          return ((select(T(), n << offset, offset < 32u) & mask) | (v & ~(mask)));
-        //      }
-
-        tint::Vector<const Statement*, 8> body;
-
-        switch (cfg.builtins.insert_bits) {
-            case Level::kFull:
-                // let e = offset + count;
-                body.Push(b.Decl(b.Let("e", b.Add("offset", "count"))));
-
-                // let mask = (
-                //              (select(0u, 1u << offset, offset < 32u) - 1u) ^
-                //              (select(0u, 1u << e, e < 32u) - 1u)
-                //            );
-                body.Push(b.Decl(b.Let(
-                    "mask",
-                    b.Xor(  //
-                        b.Sub(
-                            b.Call("select", 0_u, b.Shl(1_u, "offset"), b.LessThan("offset", 32_u)),
-                            1_u),
-                        b.Sub(b.Call("select", 0_u, b.Shl(1_u, "e"), b.LessThan("e", 32_u)),
-                              1_u)  //
-                        ))));
-
-                // return ((select(T(), n << offset, offset < 32u) & mask) | (v & ~(mask)));
-                body.Push(
-                    b.Return(b.Or(b.And(b.Call("select", b.Call(T(ty)), b.Shl("n", U("offset")),
-                                               b.LessThan("offset", 32_u)),
-                                        V("mask")),
-                                  b.And("v", V(b.Complement("mask"))))));
-
-                break;
-            case Level::kClampParameters:
-                body.Push(b.Decl(b.Let("s", b.Call("min", "offset", u32(W)))));
-                body.Push(b.Decl(b.Let("e", b.Call("min", u32(W), b.Add("s", "count")))));
-                body.Push(b.Return(b.Call("insertBits", "v", "n", "s", b.Sub("e", "s"))));
-                break;
-            default:
-                TINT_ICE() << "unhandled polyfill level: "
-                           << static_cast<int>(cfg.builtins.insert_bits);
-        }
-
-        b.Func(name,
-               tint::Vector{
-                   b.Param("v", T(ty)),
-                   b.Param("n", T(ty)),
-                   b.Param("offset", b.ty.u32()),
-                   b.Param("count", b.ty.u32()),
-               },
-               T(ty), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `reflect` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol reflect(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_reflect");
-
-        // WGSL polyfill function:
-        //      fn tint_reflect(e1 : T, e2 : T) -> T {
-        //          let factor = (-2.0 * dot(e1, e2));
-        //          return (e1 + (factor * e2));
-        //      }
-        // Using -2.0 instead of 2.0 in factor to prevent the optimization that cause wrong result.
-        // See https://crbug.com/tint/1798 for more details.
-        auto body = tint::Vector{
-            b.Decl(b.Let("factor", b.Mul(-2.0_a, b.Call("dot", "e1", "e2")))),
-            b.Return(b.Add("e1", b.Mul("factor", "e2"))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("e1", T(ty)),
-                   b.Param("e2", T(ty)),
-               },
-               T(ty), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `saturate` builtin
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol saturate(const core::type::Type* ty) {
-        auto name = b.Symbols().New("tint_saturate");
-        auto body = tint::Vector{
-            b.Return(b.Call("clamp", "v", b.Call(T(ty), 0_a), b.Call(T(ty), 1_a))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("v", T(ty)),
-               },
-               T(ty), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `sign` builtin when the element type is integer
-    /// @param ty the parameter and return type for the function
-    /// @return the polyfill function name
-    Symbol sign_int(const core::type::Type* ty) {
-        const uint32_t width = WidthOf(ty);
-        auto zero = [&] { return ScalarOrVector(width, 0_a); };
-
-        // pos_or_neg_one = (v > 0) ? 1 : -1
-        auto pos_or_neg_one = b.Call("select",                     //
-                                     ScalarOrVector(width, -1_a),  //
-                                     ScalarOrVector(width, 1_a),   //
-                                     b.GreaterThan("v", zero()));
-
-        auto name = b.Symbols().New("tint_sign");
-        b.Func(name,
-               tint::Vector{
-                   b.Param("v", T(ty)),
-               },
-               T(ty),
-               tint::Vector{
-                   b.Return(b.Call("select", pos_or_neg_one, zero(), b.Equal("v", zero()))),
-               });
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `textureSampleBaseClampToEdge` builtin, when the
-    /// texture type is texture_2d<f32>.
-    /// @return the polyfill function name
-    Symbol textureSampleBaseClampToEdge_2d_f32() {
-        auto name = b.Symbols().New("tint_textureSampleBaseClampToEdge");
-        auto body = tint::Vector{
-            b.Decl(b.Let("dims", b.Call(b.ty.vec2<f32>(), b.Call("textureDimensions", "t", 0_a)))),
-            b.Decl(b.Let("half_texel", b.Div(b.Call<vec2<f32>>(0.5_a), "dims"))),
-            b.Decl(
-                b.Let("clamped", b.Call("clamp", "coord", "half_texel", b.Sub(1_a, "half_texel")))),
-            b.Return(b.Call("textureSampleLevel", "t", "s", "clamped", 0_a)),
-        };
-        b.Func(
-            name,
-            tint::Vector{
-                b.Param("t", b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
-                b.Param("s", b.ty.sampler(core::type::SamplerKind::kSampler)),
-                b.Param("coord", b.ty.vec2<f32>()),
-            },
-            b.ty.vec4<f32>(), body);
-        return name;
-    }
-
-    /// Builds the polyfill function for the `quantizeToF16` builtin, by replacing the vector form
-    /// with scalar calls.
-    /// @param vec the vector type
-    /// @return the polyfill function name
-    Symbol quantizeToF16(const core::type::Vector* vec) {
-        auto name = b.Symbols().New("tint_quantizeToF16");
-        tint::Vector<const Expression*, 4> args;
-        for (uint32_t i = 0; i < vec->Width(); i++) {
-            args.Push(b.Call("quantizeToF16", b.IndexAccessor("v", u32(i))));
-        }
-        b.Func(name,
-               tint::Vector{
-                   b.Param("v", T(vec)),
-               },
-               T(vec),
-               tint::Vector{
-                   b.Return(b.Call(T(vec), std::move(args))),
-               });
-        return name;
-    }
-
-    /// Builds the polyfill function for the `workgroupUniformLoad` builtin.
-    /// @param type the type being loaded
-    /// @return the polyfill function name
-    Symbol workgroupUniformLoad(const core::type::Type* type) {
-        auto name = b.Symbols().New("tint_workgroupUniformLoad");
-        if (auto* a = type->As<core::type::Atomic>()) {
-            b.Func(name,
-                   tint::Vector{
-                       b.Param("p", b.ty.ptr<workgroup>(T(type))),
-                   },
-                   T(a->Type()),
-                   tint::Vector{
-                       b.CallStmt(b.Call("workgroupBarrier")),
-                       b.Decl(b.Let("result", b.Call("atomicLoad", b.Expr("p")))),
-                       b.CallStmt(b.Call("workgroupBarrier")),
-                       b.Return("result"),
-                   });
-            return name;
-        }
-        b.Func(name,
-               tint::Vector{
-                   b.Param("p", b.ty.ptr<workgroup>(T(type))),
-               },
-               T(type),
-               tint::Vector{
-                   b.CallStmt(b.Call("workgroupBarrier")),
-                   b.Decl(b.Let("result", b.Deref("p"))),
-                   b.CallStmt(b.Call("workgroupBarrier")),
-                   b.Return("result"),
-               });
-        return name;
-    }
-
-    /// Builds the polyfill function to value convert a scalar or vector of f32 to an i32 or u32 (or
-    /// vector of).
-    /// @param source the type of the value being converted
-    /// @param target the target conversion type
-    /// @return the polyfill function name
-    Symbol ConvF32ToIU32(const core::type::Type* source, const core::type::Type* target) {
-        struct Limits {
-            AFloat low_condition;
-            AInt low_limit;
-            AFloat high_condition;
-            AInt high_limit;
-        };
-        const bool is_signed = target->IsSignedIntegerScalarOrVector();
-        const uint32_t largest_signed_integer_float = 0x7fffff80;
-        const uint32_t largest_unsigned_integer_float = 0xffffff00;
-        const Limits limits =
-            is_signed ? Limits{
-                            /* low_condition   */ -AFloat(0x80000000),
-                            /* low_limit  */ -AInt(0x80000000),
-                            /* high_condition  */ AFloat(largest_signed_integer_float),
-                            /* high_limit */ AInt(0x7fffffff),
-                        }
-                      : Limits{
-                            /* low_condition   */ AFloat(0),
-                            /* low_limit  */ AInt(0),
-                            /* high_condition  */ AFloat(largest_unsigned_integer_float),
-                            /* high_limit */ AInt(0xffffffff),
-                        };
-
-        const uint32_t width = WidthOf(target);
-
-        // select(target(v), low_limit, v < low_condition)
-        auto* select_low = b.Call(wgsl::BuiltinFn::kSelect,                 //
-                                  b.Call(T(target), "v"),                   //
-                                  ScalarOrVector(width, limits.low_limit),  //
-                                  b.LessThan("v", ScalarOrVector(width, limits.low_condition)));
-
-        // select(high_limit, select_low, v <= high_condition)
-        // The equality test in the 'LessThanEqual' is used to ensure that the largest integer float
-        // will be converted to an integer.
-        auto* select_high =
-            b.Call(wgsl::BuiltinFn::kSelect,                  //
-                   ScalarOrVector(width, limits.high_limit),  //
-                   select_low,                                //
-                   b.LessThanEqual("v", ScalarOrVector(width, limits.high_condition)));
-
-        auto name = b.Symbols().New(is_signed ? "tint_ftoi" : "tint_ftou");
-        b.Func(name, tint::Vector{b.Param("v", T(source))}, T(target),
-               tint::Vector{b.Return(select_high)});
-        return name;
-    }
-
-    /// Builds the polyfill function for the `dot4I8Packed` builtin
-    /// @return the polyfill function name
-    Symbol Dot4I8Packed() {
-        using vec4i = vec4<i32>;
-        using vec4u = vec4<u32>;
-
-        auto name = b.Symbols().New("tint_dot4_i8_packed");
-
-        auto body = tint::Vector{
-            // const n = vec4u(24, 16, 8, 0);
-            // let a_i8 = bitcast<vec4i>(vec4u(a) << n) >> vec4u(24);
-            // let b_i8 = bitcast<vec4i>(vec4u(b) << n) >> vec4u(24);
-            // return dot(a_i8, b_i8);
-            b.Decl(b.Const("n", b.Call<vec4u>(24_a, 16_a, 8_a, 0_a))),
-            b.Decl(b.Let("a_i8", b.Shr(b.Bitcast<vec4i>(b.Shl(b.Call<vec4u>("a"), "n")),
-                                       b.Call<vec4u>(24_a)))),
-            b.Decl(b.Let("b_i8", b.Shr(b.Bitcast<vec4i>(b.Shl(b.Call<vec4u>("b"), "n")),
-                                       b.Call<vec4u>(24_a)))),
-            b.Return(b.Call("dot", "a_i8", "b_i8")),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("a", b.ty.u32()),
-                   b.Param("b", b.ty.u32()),
-               },
-               b.ty.i32(), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `dot4U8Packed` builtin
-    /// @return the polyfill function name
-    Symbol Dot4U8Packed() {
-        using vec4u = vec4<u32>;
-        auto name = b.Symbols().New("tint_dot4_u8_packed");
-
-        auto body = tint::Vector{
-            // const n = vec4u(24, 16, 8, 0);
-            // let a_u8 = (vec4u(a) >> n) & vec4u(0xff);
-            // let b_u8 = (vec4u(b) >> n) & vec4u(0xff);
-            // return dot(a_u8, b_u8);
-            b.Decl(b.Const("n", b.Call<vec4u>(24_a, 16_a, 8_a, 0_a))),
-            b.Decl(b.Let("a_u8", b.And(b.Shr(b.Call<vec4u>("a"), "n"), b.Call<vec4u>(0xff_a)))),
-            b.Decl(b.Let("b_u8", b.And(b.Shr(b.Call<vec4u>("b"), "n"), b.Call<vec4u>(0xff_a)))),
-            b.Return(b.Call("dot", "a_u8", "b_u8")),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("a", b.ty.u32()),
-                   b.Param("b", b.ty.u32()),
-               },
-               b.ty.u32(), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `pack4xI8` builtin
-    /// @return the polyfill function name
-    Symbol Pack4xI8() {
-        using vec4u = vec4<u32>;
-
-        auto name = b.Symbols().New("tint_pack_4xi8");
-
-        auto body = tint::Vector{
-            // const n = vec4u(0, 8, 16, 24);
-            // let a_u32 = bitcast<vec4u>(a);
-            // let a_u8 = (a_u32 & vec4u(0xff)) << n;
-            // return dot(a_u8, vec4u(1));
-            b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
-            b.Decl(b.Let("a_u32", b.Bitcast<vec4u>("a"))),
-            b.Decl(b.Let("a_u8", b.Shl(b.And("a_u32", b.Call<vec4u>(0xff_a)), "n"))),
-            b.Return(b.Call("dot", "a_u8", b.Call<vec4u>(1_a))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("a", b.ty.vec4<i32>()),
-               },
-               b.ty.u32(), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `pack4xU8` builtin
-    /// @return the polyfill function name
-    Symbol Pack4xU8() {
-        using vec4u = vec4<u32>;
-
-        auto name = b.Symbols().New("tint_pack_4xu8");
-
-        auto body = tint::Vector{
-            // const n = vec4u(0, 8, 16, 24);
-            // let a_u8 = (a & vec4u(0xff)) << n;
-            // return dot(a_u8, vec4u(1));
-            b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
-            b.Decl(b.Let("a_u8", b.Shl(b.And("a", b.Call<vec4u>(0xff_a)), "n"))),
-            b.Return(b.Call("dot", "a_u8", b.Call<vec4u>(1_a))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("a", b.ty.vec4<u32>()),
-               },
-               b.ty.u32(), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `pack4xI8Clamp` builtin
-    /// @return the polyfill function name
-    Symbol Pack4xI8Clamp() {
-        using vec4i = vec4<i32>;
-        using vec4u = vec4<u32>;
-
-        auto name = b.Symbols().New("tint_pack_4xi8_clamp");
-
-        auto body = tint::Vector{
-            // const n = vec4u(0, 8, 16, 24);
-            // let a_clamp = clamp(a, vec4i(-128), vec4i(127));
-            // let a_u32 = bitcast<vec4u>(a_clamp);
-            // let a_u8 = (a_u32 & vec4u(0xff)) << n;
-            // return dot(a_u8, vec4u(1));
-            b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
-            b.Decl(b.Let("a_clamp",
-                         b.Call("clamp", "a", b.Call<vec4i>(-128_a), b.Call<vec4i>(127_a)))),
-            b.Decl(b.Let("a_u32", b.Bitcast<vec4u>("a_clamp"))),
-            b.Decl(b.Let("a_u8", b.Shl(b.And("a_u32", b.Call<vec4u>(0xff_a)), "n"))),
-            b.Return(b.Call("dot", "a_u8", b.Call<vec4u>(1_a))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("a", b.ty.vec4<i32>()),
-               },
-               b.ty.u32(), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `pack4xU8Clamp` builtin
-    /// @return the polyfill function name
-    Symbol Pack4xU8Clamp() {
-        using vec4u = vec4<u32>;
-
-        auto name = b.Symbols().New("tint_pack_4xu8_clamp");
-
-        auto body = tint::Vector{
-            // const n = vec4u(0, 8, 16, 24);
-            // let a_clamp = clamp(a, vec4u(0), vec4u(255));
-            // let a_u8 = a_clamp << n;
-            // return dot(a_u8, vec4u(1));
-            b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
-            b.Decl(
-                b.Let("a_clamp", b.Call("clamp", "a", b.Call<vec4u>(0_a), b.Call<vec4u>(255_a)))),
-            b.Decl(b.Let("a_u8", b.Call<vec4u>(b.Shl("a_clamp", "n")))),
-            b.Return(b.Call("dot", "a_u8", b.Call<vec4u>(1_a))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("a", b.ty.vec4<u32>()),
-               },
-               b.ty.u32(), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `unpack4xI8` builtin
-    /// @return the polyfill function name
-    Symbol Unpack4xI8() {
-        using vec4i = vec4<i32>;
-        using vec4u = vec4<u32>;
-
-        auto name = b.Symbols().New("tint_unpack_4xi8");
-
-        auto body = tint::Vector{
-            // const n = vec4u(24, 16, 8, 0);
-            // let a_vec4u = vec4u(a);
-            // let a_vec4i = bitcast<vec4i>(a_vec4u << n);
-            // return a_vec4i >> vec4u(24);
-            b.Decl(b.Const("n", b.Call<vec4u>(24_a, 16_a, 8_a, 0_a))),
-            b.Decl(b.Let("a_vec4u", b.Call<vec4u>("a"))),
-            b.Decl(b.Let("a_vec4i", b.Bitcast<vec4i>(b.Shl("a_vec4u", "n")))),
-            b.Return(b.Shr("a_vec4i", b.Call<vec4u>(24_a))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("a", b.ty.u32()),
-               },
-               b.ty.vec4<i32>(), body);
-
-        return name;
-    }
-
-    /// Builds the polyfill function for the `unpack4xU8` builtin
-    /// @return the polyfill function name
-    Symbol Unpack4xU8() {
-        using vec4u = vec4<u32>;
-
-        auto name = b.Symbols().New("tint_unpack_4xu8");
-
-        auto body = tint::Vector{
-            // const n = vec4u(0, 8, 16, 24);
-            // const a_vec4u = vec4u(a) >> n;
-            // return a_vec4u & vec4u(0xff);
-            b.Decl(b.Const("n", b.Call<vec4u>(0_a, 8_a, 16_a, 24_a))),
-            b.Decl(b.Let("a_vec4u", b.Shr(b.Call<vec4u>("a"), "n"))),
-            b.Return(b.And("a_vec4u", b.Call<vec4u>(0xff_a))),
-        };
-        b.Func(name,
-               tint::Vector{
-                   b.Param("a", b.ty.u32()),
-               },
-               b.ty.vec4<u32>(), body);
-
-        return name;
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // Inline polyfills
-    ////////////////////////////////////////////////////////////////////////////
-
-    /// Builds the polyfill inline expression for a bitshift left or bitshift right, ensuring that
-    /// the RHS is modulo the bit-width of the LHS.
-    /// @param bin_op the original BinaryExpression
-    /// @return the polyfill value for bitshift operation
-    const Expression* BitshiftModulo(const BinaryExpression* bin_op) {
-        auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
-        auto* rhs_ty = src.TypeOf(bin_op->rhs)->UnwrapRef();
-        auto* lhs_el_ty = lhs_ty->DeepestElement();
-        const Expression* mask = b.Expr(AInt(lhs_el_ty->Size() * 8 - 1));
-        if (rhs_ty->Is<core::type::Vector>()) {
-            mask = b.Call(CreateASTTypeFor(ctx, rhs_ty), mask);
-        }
-        auto* lhs = ctx.Clone(bin_op->lhs);
-        auto* rhs = b.And(ctx.Clone(bin_op->rhs), mask);
-        return b.create<BinaryExpression>(ctx.Clone(bin_op->source), bin_op->op, lhs, rhs);
-    }
-
-    /// Builds the polyfill inline expression for a integer divide or modulo, preventing DBZs and
-    /// integer overflows.
-    /// @param bin_op the original BinaryExpression
-    /// @return the polyfill divide or modulo
-    const Expression* IntDivMod(const BinaryExpression* bin_op) {
-        auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
-        auto* rhs_ty = src.TypeOf(bin_op->rhs)->UnwrapRef();
-        BinaryOpSignature sig{bin_op->op, lhs_ty, rhs_ty};
-        auto fn = binary_op_polyfills.GetOrAdd(sig, [&] {
-            const bool is_div = bin_op->op == core::BinaryOp::kDivide;
-
-            const auto [lhs_el_ty, lhs_width] = lhs_ty->Elements(lhs_ty, 1);
-            const auto [rhs_el_ty, rhs_width] = rhs_ty->Elements(rhs_ty, 1);
-
-            const uint32_t width = std::max(lhs_width, rhs_width);
-
-            const char* lhs = "lhs";
-            const char* rhs = "rhs";
-
-            tint::Vector<const Statement*, 4> body;
-
-            if (lhs_width < width) {
-                // lhs is scalar, rhs is vector. Convert lhs to vector.
-                body.Push(b.Decl(b.Let("l", b.vec(T(lhs_el_ty), width, b.Expr(lhs)))));
-                lhs = "l";
-            }
-            if (rhs_width < width) {
-                // lhs is vector, rhs is scalar. Convert rhs to vector.
-                body.Push(b.Decl(b.Let("r", b.vec(T(rhs_el_ty), width, b.Expr(rhs)))));
-                rhs = "r";
-            }
-
-            auto name = b.Symbols().New(is_div ? "tint_div" : "tint_mod");
-
-            auto* rhs_is_zero = b.Equal(rhs, ScalarOrVector(width, 0_a));
-
-            if (lhs_ty->IsSignedIntegerScalarOrVector()) {
-                const auto bits = lhs_el_ty->Size() * 8;
-                auto min_int = AInt(AInt::kLowestValue >> (AInt::kNumBits - bits));
-                const Expression* lhs_is_min = b.Equal(lhs, ScalarOrVector(width, min_int));
-                const Expression* rhs_is_minus_one = b.Equal(rhs, ScalarOrVector(width, -1_a));
-                // use_one = rhs_is_zero | ((lhs == MIN_INT) & (rhs == -1))
-                auto* use_one = b.Or(rhs_is_zero, b.And(lhs_is_min, rhs_is_minus_one));
-
-                // Special handling for mod in case either operand is negative, as negative operands
-                // for % is undefined behaviour for most backends (HLSL, MSL, GLSL, SPIR-V).
-                if (!is_div) {
-                    const char* rhs_or_one = "rhs_or_one";
-                    body.Push(b.Decl(b.Let(
-                        rhs_or_one, b.Call("select", rhs, ScalarOrVector(width, 1_a), use_one))));
-
-                    // Is either operand negative?
-                    // (lhs | rhs) & (1<<31)
-                    auto sign_bit_mask = ScalarOrVector(width, u32(1 << (bits - 1)));
-                    auto* lhs_or_rhs = CastScalarOrVector<u32>(width, b.Or(lhs, rhs_or_one));
-                    auto* lhs_or_rhs_is_neg =
-                        b.NotEqual(b.And(lhs_or_rhs, sign_bit_mask), ScalarOrVector(width, 0_u));
-
-                    // lhs - trunc(lhs / rhs) * rhs (note: integral division truncates)
-                    auto* slow_mod = b.Sub(lhs, b.Mul(b.Div(lhs, rhs_or_one), rhs_or_one));
-
-                    // lhs % rhs
-                    auto* fast_mod = b.Mod(lhs, rhs_or_one);
-
-                    auto* use_slow = b.Call("any", lhs_or_rhs_is_neg);
-
-                    body.Push(b.If(use_slow, b.Block(b.Return(slow_mod)),
-                                   b.Else(b.Block(b.Return(fast_mod)))));
-
-                } else {
-                    auto* rhs_or_one = b.Call("select", rhs, ScalarOrVector(width, 1_a), use_one);
-                    body.Push(b.Return(is_div ? b.Div(lhs, rhs_or_one) : b.Mod(lhs, rhs_or_one)));
-                }
-
-            } else {
-                auto* rhs_or_one = b.Call("select", rhs, ScalarOrVector(width, 1_a), rhs_is_zero);
-                body.Push(b.Return(is_div ? b.Div(lhs, rhs_or_one) : b.Mod(lhs, rhs_or_one)));
-            }
-
-            b.Func(name,
-                   tint::Vector{
-                       b.Param("lhs", T(lhs_ty)),
-                       b.Param("rhs", T(rhs_ty)),
-                   },
-                   width == 1 ? T(lhs_ty) : b.ty.vec(T(lhs_el_ty), width),  // return type
-                   std::move(body));
-
-            return name;
-        });
-        auto* lhs = ctx.Clone(bin_op->lhs);
-        auto* rhs = ctx.Clone(bin_op->rhs);
-        return b.Call(fn, lhs, rhs);
-    }
-
-    /// Builds the polyfill inline expression for a precise float modulo, as defined in the spec.
-    /// @param bin_op the original BinaryExpression
-    /// @return the polyfill divide or modulo
-    const Expression* PreciseFloatMod(const BinaryExpression* bin_op) {
-        auto* lhs_ty = src.TypeOf(bin_op->lhs)->UnwrapRef();
-        auto* rhs_ty = src.TypeOf(bin_op->rhs)->UnwrapRef();
-        BinaryOpSignature sig{bin_op->op, lhs_ty, rhs_ty};
-        auto fn = binary_op_polyfills.GetOrAdd(sig, [&] {
-            const auto [lhs_el_ty, lhs_width] = lhs_ty->Elements(lhs_ty, 1);
-            const auto [rhs_el_ty, rhs_width] = rhs_ty->Elements(rhs_ty, 1);
-
-            const uint32_t width = std::max(lhs_width, rhs_width);
-
-            const char* lhs = "lhs";
-            const char* rhs = "rhs";
-
-            tint::Vector<const Statement*, 4> body;
-
-            if (lhs_width < width) {
-                // lhs is scalar, rhs is vector. Convert lhs to vector.
-                body.Push(b.Decl(b.Let("l", b.vec(T(lhs_el_ty), width, b.Expr(lhs)))));
-                lhs = "l";
-            }
-            if (rhs_width < width) {
-                // lhs is vector, rhs is scalar. Convert rhs to vector.
-                body.Push(b.Decl(b.Let("r", b.vec(T(rhs_el_ty), width, b.Expr(rhs)))));
-                rhs = "r";
-            }
-
-            auto name = b.Symbols().New("tint_float_mod");
-
-            // lhs - trunc(lhs / rhs) * rhs
-            auto* precise_mod = b.Sub(lhs, b.Mul(b.Call("trunc", b.Div(lhs, rhs)), rhs));
-            body.Push(b.Return(precise_mod));
-
-            b.Func(name,
-                   tint::Vector{
-                       b.Param("lhs", T(lhs_ty)),
-                       b.Param("rhs", T(rhs_ty)),
-                   },
-                   width == 1 ? T(lhs_ty) : b.ty.vec(T(lhs_el_ty), width),  // return type
-                   std::move(body));
-
-            return name;
-        });
-        auto* lhs = ctx.Clone(bin_op->lhs);
-        auto* rhs = ctx.Clone(bin_op->rhs);
-        return b.Call(fn, lhs, rhs);
-    }
-
-    /// @returns the AST type for the given sem type
-    Type T(const core::type::Type* ty) { return CreateASTTypeFor(ctx, ty); }
-
-    /// @returns 1 if `ty` is not a vector, otherwise the vector width
-    uint32_t WidthOf(const core::type::Type* ty) const {
-        if (auto* v = ty->As<core::type::Vector>()) {
-            return v->Width();
-        }
-        return 1;
-    }
-
-    /// @returns a scalar or vector with the given width, with each element with
-    /// the given value.
-    template <typename T>
-    const Expression* ScalarOrVector(uint32_t width, T value) {
-        if (width == 1) {
-            return b.Expr(value);
-        }
-        return b.Call(b.ty.vec<T>(width), value);
-    }
-
-    template <typename To>
-    const Expression* CastScalarOrVector(uint32_t width, const Expression* e) {
-        if (width == 1) {
-            return b.Call(b.ty.Of<To>(), e);
-        }
-        return b.Call(b.ty.vec<To>(width), e);
-    }
-
-    /// Examines the call expression @p expr, applying any necessary polyfill transforms
-    void Call(const CallExpression* expr) {
-        auto* call = src.Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
-        if (!call || call->Stage() != core::EvaluationStage::kRuntime) {
-            return;  // Only polyfill runtime expressions
-        }
-        Symbol fn = Switch(
-            call->Target(),  //
-            [&](const sem::BuiltinFn* builtin) {
-                switch (builtin->Fn()) {
-                    case wgsl::BuiltinFn::kAcosh:
-                        if (cfg.builtins.acosh != Level::kNone) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return acosh(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kAsinh:
-                        if (cfg.builtins.asinh) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return asinh(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kAtanh:
-                        if (cfg.builtins.atanh != Level::kNone) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return atanh(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kClamp:
-                        if (cfg.builtins.clamp_int) {
-                            auto& sig = builtin->Signature();
-                            if (sig.parameters[0]->Type()->IsIntegerScalarOrVector()) {
-                                return builtin_polyfills.GetOrAdd(
-                                    builtin, [&] { return clampInteger(builtin->ReturnType()); });
-                            }
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kCountLeadingZeros:
-                        if (cfg.builtins.count_leading_zeros) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return countLeadingZeros(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kCountTrailingZeros:
-                        if (cfg.builtins.count_trailing_zeros) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return countTrailingZeros(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kExtractBits:
-                        if (cfg.builtins.extract_bits != Level::kNone) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return extractBits(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kFirstLeadingBit:
-                        if (cfg.builtins.first_leading_bit) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return firstLeadingBit(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kFirstTrailingBit:
-                        if (cfg.builtins.first_trailing_bit) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return firstTrailingBit(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kFwidthFine:
-                        if (cfg.builtins.fwidth_fine) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return fwidthFine(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kInsertBits:
-                        if (cfg.builtins.insert_bits != Level::kNone) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return insertBits(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kReflect:
-                        // Only polyfill for vec2<f32>. See https://crbug.com/tint/1798 for
-                        // more details.
-                        if (cfg.builtins.reflect_vec2_f32) {
-                            auto& sig = builtin->Signature();
-                            auto* vec = sig.return_type->As<core::type::Vector>();
-                            if (vec && vec->Width() == 2 && vec->Type()->Is<core::type::F32>()) {
-                                return builtin_polyfills.GetOrAdd(
-                                    builtin, [&] { return reflect(builtin->ReturnType()); });
-                            }
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kSaturate:
-                        if (cfg.builtins.saturate) {
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return saturate(builtin->ReturnType()); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kSign:
-                        if (cfg.builtins.sign_int) {
-                            auto* ty = builtin->ReturnType();
-                            if (ty->IsSignedIntegerScalarOrVector()) {
-                                return builtin_polyfills.GetOrAdd(builtin,
-                                                                  [&] { return sign_int(ty); });
-                            }
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kTextureLoad:
-                        if (cfg.builtins.bgra8unorm) {
-                            auto& sig = builtin->Signature();
-                            auto* tex = sig.Parameter(core::ParameterUsage::kTexture);
-                            if (auto* stex = tex->Type()->As<core::type::StorageTexture>()) {
-                                if (stex->TexelFormat() == core::TexelFormat::kBgra8Unorm) {
-                                    ctx.Replace(expr, [this, expr] {
-                                        return ctx.dst->MemberAccessor(
-                                            ctx.CloneWithoutTransform(expr), "bgra");
-                                    });
-                                    made_changes = true;
-                                }
-                            }
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kTextureSampleBaseClampToEdge:
-                        if (cfg.builtins.texture_sample_base_clamp_to_edge_2d_f32) {
-                            auto& sig = builtin->Signature();
-                            auto* tex = sig.Parameter(core::ParameterUsage::kTexture);
-                            if (auto* stex = tex->Type()->As<core::type::SampledTexture>()) {
-                                if (stex->Type()->Is<core::type::F32>()) {
-                                    return builtin_polyfills.GetOrAdd(builtin, [&] {
-                                        return textureSampleBaseClampToEdge_2d_f32();
-                                    });
-                                }
-                            }
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kTextureStore:
-                        if (cfg.builtins.bgra8unorm) {
-                            auto& sig = builtin->Signature();
-                            auto* tex = sig.Parameter(core::ParameterUsage::kTexture);
-                            if (auto* stex = tex->Type()->As<core::type::StorageTexture>()) {
-                                if (stex->TexelFormat() == core::TexelFormat::kBgra8Unorm) {
-                                    size_t value_idx = static_cast<size_t>(
-                                        sig.IndexOf(core::ParameterUsage::kValue));
-                                    ctx.Replace(expr, [this, expr, value_idx] {
-                                        tint::Vector<const Expression*, 3> args;
-                                        for (auto* arg : expr->args) {
-                                            arg = ctx.Clone(arg);
-                                            if (args.Length() == value_idx) {  // value
-                                                arg = ctx.dst->MemberAccessor(arg, "bgra");
-                                            }
-                                            args.Push(arg);
-                                        }
-                                        return ctx.dst->Call(
-                                            tint::ToString(wgsl::BuiltinFn::kTextureStore),
-                                            std::move(args));
-                                    });
-                                    made_changes = true;
-                                }
-                            }
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kWorkgroupUniformLoad:
-                        if (cfg.builtins.workgroup_uniform_load) {
-                            auto* param_type = builtin->Parameters()[0]->Type();
-                            TINT_ASSERT(param_type->Is<core::type::Pointer>());
-                            auto* ty = param_type->As<core::type::Pointer>()->StoreType();
-                            return builtin_polyfills.GetOrAdd(
-                                builtin, [&] { return workgroupUniformLoad(ty); });
-                        }
-                        return Symbol{};
-
-                    case wgsl::BuiltinFn::kDot4I8Packed: {
-                        if (cfg.builtins.dot_4x8_packed) {
-                            return builtin_polyfills.GetOrAdd(builtin,
-                                                              [&] { return Dot4I8Packed(); });
-                        }
-                        return Symbol{};
-                    }
-
-                    case wgsl::BuiltinFn::kDot4U8Packed: {
-                        if (cfg.builtins.dot_4x8_packed) {
-                            return builtin_polyfills.GetOrAdd(builtin,
-                                                              [&] { return Dot4U8Packed(); });
-                        }
-                        return Symbol{};
-                    }
-
-                    case wgsl::BuiltinFn::kPack4XI8: {
-                        if (cfg.builtins.pack_unpack_4x8) {
-                            return builtin_polyfills.GetOrAdd(builtin, [&] { return Pack4xI8(); });
-                        }
-                        return Symbol{};
-                    }
-
-                    case wgsl::BuiltinFn::kPack4XU8: {
-                        if (cfg.builtins.pack_unpack_4x8) {
-                            return builtin_polyfills.GetOrAdd(builtin, [&] { return Pack4xU8(); });
-                        }
-                        return Symbol{};
-                    }
-
-                    case wgsl::BuiltinFn::kPack4XI8Clamp: {
-                        if (cfg.builtins.pack_unpack_4x8) {
-                            return builtin_polyfills.GetOrAdd(builtin,
-                                                              [&] { return Pack4xI8Clamp(); });
-                        }
-                        return Symbol{};
-                    }
-
-                    case wgsl::BuiltinFn::kPack4XU8Clamp: {
-                        if (cfg.builtins.pack_4xu8_clamp) {
-                            return builtin_polyfills.GetOrAdd(builtin,
-                                                              [&] { return Pack4xU8Clamp(); });
-                        }
-                        return Symbol{};
-                    }
-
-                    case wgsl::BuiltinFn::kUnpack4XI8: {
-                        if (cfg.builtins.pack_unpack_4x8) {
-                            return builtin_polyfills.GetOrAdd(builtin,
-                                                              [&] { return Unpack4xI8(); });
-                        }
-                        return Symbol{};
-                    }
-
-                    case wgsl::BuiltinFn::kUnpack4XU8: {
-                        if (cfg.builtins.pack_unpack_4x8) {
-                            return builtin_polyfills.GetOrAdd(builtin,
-                                                              [&] { return Unpack4xU8(); });
-                        }
-                        return Symbol{};
-                    }
-
-                    default:
-                        return Symbol{};
-                }
-            },
-            [&](const sem::ValueConversion* conv) {
-                if (cfg.builtins.conv_f32_to_iu32) {
-                    auto* src_ty = conv->Source();
-                    if (tint::Is<core::type::F32>(src_ty->Elements(src_ty).type)) {
-                        auto* dst_ty = conv->Target();
-                        if (tint::IsAnyOf<core::type::I32, core::type::U32>(
-                                dst_ty->Elements(dst_ty).type)) {
-                            return f32_conv_polyfills.GetOrAdd(dst_ty, [&] {  //
-                                return ConvF32ToIU32(src_ty, dst_ty);
-                            });
-                        }
-                    }
-                }
-                return Symbol{};
-            });
-
-        if (fn.IsValid()) {
-            ctx.Replace(call->Declaration(),
-                        [this, fn, expr] { return ctx.dst->Call(fn, ctx.Clone(expr->args)); });
-            made_changes = true;
-        }
-    }
-};
-
-BuiltinPolyfill::BuiltinPolyfill() = default;
-
-BuiltinPolyfill::~BuiltinPolyfill() = default;
-
-Transform::ApplyResult BuiltinPolyfill::Apply(const Program& src,
-                                              const DataMap& data,
-                                              DataMap&) const {
-    auto* cfg = data.Get<Config>();
-    if (!cfg) {
-        return SkipTransform;
-    }
-    return State{src, *cfg}.Run();
-}
-
-BuiltinPolyfill::Config::Config() = default;
-BuiltinPolyfill::Config::Config(const Builtins& b) : builtins(b) {}
-BuiltinPolyfill::Config::Config(const Config&) = default;
-BuiltinPolyfill::Config::~Config() = default;
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h b/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h
deleted file mode 100644
index 9c01106..0000000
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill.h
+++ /dev/null
@@ -1,179 +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_WGSL_AST_TRANSFORM_BUILTIN_POLYFILL_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_BUILTIN_POLYFILL_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// Implements builtins for backends that do not have a native implementation.
-class BuiltinPolyfill final : public Castable<BuiltinPolyfill, Transform> {
-  public:
-    /// Constructor
-    BuiltinPolyfill();
-    /// Destructor
-    ~BuiltinPolyfill() override;
-
-    /// Enumerator of polyfill levels
-    enum class Level {
-        /// No polyfill needed, supported by the backend.
-        kNone,
-        /// Clamp the parameters to the inner implementation.
-        kClampParameters,
-        /// Range check the input.
-        kRangeCheck,
-        /// Polyfill the entire function
-        kFull,
-    };
-
-    /// Specifies the builtins that should be polyfilled by the transform.
-    struct Builtins {
-        /// What level should `acosh` be polyfilled?
-        Level acosh = Level::kNone;
-        /// Should `asinh` be polyfilled?
-        bool asinh = false;
-        /// What level should `atanh` be polyfilled?
-        Level atanh = Level::kNone;
-        /// Should storage textures of format 'bgra8unorm' be replaced with 'rgba8unorm'?
-        bool bgra8unorm = false;
-        /// Should the RHS of `<<` and `>>` be wrapped in a modulo bit-width of LHS?
-        bool bitshift_modulo = false;
-        /// Should `clamp()` be polyfilled for integer values (scalar or vector)?
-        bool clamp_int = false;
-        /// Should `countLeadingZeros()` be polyfilled?
-        bool count_leading_zeros = false;
-        /// Should `countTrailingZeros()` be polyfilled?
-        bool count_trailing_zeros = false;
-        /// Should converting f32 to i32 or u32 be polyfilled?
-        bool conv_f32_to_iu32 = false;
-        /// What level should `extractBits()` be polyfilled?
-        Level extract_bits = Level::kNone;
-        /// Should `firstLeadingBit()` be polyfilled?
-        bool first_leading_bit = false;
-        /// Should `firstTrailingBit()` be polyfilled?
-        bool first_trailing_bit = false;
-        /// Should `fwidthFine()` be polyfilled?
-        bool fwidth_fine = false;
-        /// Should `insertBits()` be polyfilled?
-        Level insert_bits = Level::kNone;
-        /// Should integer scalar / vector divides and modulos be polyfilled to avoid DBZ and
-        /// integer overflows?
-        bool int_div_mod = false;
-        /// Should float modulos be polyfilled to emit a precise modulo operation as per the spec?
-        bool precise_float_mod = false;
-        /// Should `reflect()` be polyfilled for vec2<f32>?
-        bool reflect_vec2_f32 = false;
-        /// Should `saturate()` be polyfilled?
-        bool saturate = false;
-        /// Should `sign()` be polyfilled for integer types?
-        bool sign_int = false;
-        /// Should `textureSampleBaseClampToEdge()` be polyfilled for texture_2d<f32> textures?
-        bool texture_sample_base_clamp_to_edge_2d_f32 = false;
-        /// Should `workgroupUniformLoad()` be polyfilled?
-        bool workgroup_uniform_load = false;
-        /// Should `dot4I8Packed()` and `dot4U8Packed()` be polyfilled?
-        bool dot_4x8_packed = false;
-        /// Should `pack4xI8()`, `pack4xU8()`, `pack4xI8Clamp()`, `unpack4xI8()` and `unpack4xU8()`
-        /// be polyfilled?
-        bool pack_unpack_4x8 = false;
-        /// Should `pack4xU8Clamp()` be polyfilled?
-        /// TODO(tint:1497): remove the option once the bug in DXC is fixed.
-        bool pack_4xu8_clamp = false;
-
-        /// Reflection for this struct
-        TINT_REFLECT(Builtins,
-                     acosh,
-                     asinh,
-                     atanh,
-                     bgra8unorm,
-                     bitshift_modulo,
-                     clamp_int,
-                     count_leading_zeros,
-                     count_trailing_zeros,
-                     conv_f32_to_iu32,
-                     extract_bits,
-                     first_leading_bit,
-                     first_trailing_bit,
-                     fwidth_fine,
-                     insert_bits,
-                     int_div_mod,
-                     precise_float_mod,
-                     reflect_vec2_f32,
-                     saturate,
-                     sign_int,
-                     texture_sample_base_clamp_to_edge_2d_f32,
-                     workgroup_uniform_load,
-                     dot_4x8_packed,
-                     pack_unpack_4x8,
-                     pack_4xu8_clamp);
-    };
-
-    /// Config is consumed by the BuiltinPolyfill transform.
-    /// Config specifies the builtins that should be polyfilled.
-    struct Config final : public Castable<Config, Data> {
-        /// Constructor
-        Config();
-
-        /// Constructor
-        /// @param b the list of builtins to polyfill
-        explicit Config(const Builtins& b);
-
-        /// Copy constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// The builtins to polyfill
-        Builtins builtins;
-
-        /// Reflection for this struct
-        TINT_REFLECT(Config, builtins);
-    };
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::ast::transform
-
-namespace tint {
-
-/// Level reflection information
-TINT_REFLECT_ENUM_RANGE(ast::transform::BuiltinPolyfill::Level, kNone, kFull);
-
-}  // namespace tint
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_BUILTIN_POLYFILL_H_
diff --git a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc b/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
deleted file mode 100644
index 4aa809d..0000000
--- a/src/tint/lang/wgsl/ast/transform/builtin_polyfill_test.cc
+++ /dev/null
@@ -1,4340 +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/wgsl/ast/transform/builtin_polyfill.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using Level = BuiltinPolyfill::Level;
-
-using BuiltinPolyfillTest = TransformTest;
-
-TEST_F(BuiltinPolyfillTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRun_OverrideCall) {
-    auto* src = R"(
-override x = 42.123;
-override y = saturate(x);
-)";
-
-    DataMap data;
-    BuiltinPolyfill::Builtins builtins;
-    builtins.saturate = true;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, data));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRun_OverrideBinary) {
-    auto* src = R"(
-override v = 10i;
-override x = 20i / v;
-)";
-
-    DataMap data;
-    BuiltinPolyfill::Builtins builtins;
-    builtins.int_div_mod = true;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, data));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// acosh
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillAcosh(Level level) {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.acosh = level;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunAcosh) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  _ = acosh(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kNone)));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck)));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : f32 = acosh(1.0);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_Full_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : f32 = acosh(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_acosh(x : f32) -> f32 {
-  return log((x + sqrt(((x * x) - 1))));
-}
-
-fn f() {
-  let v = 1.0;
-  let r : f32 = tint_acosh(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_Full_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = acosh(vec3<f32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_acosh(x : vec3<f32>) -> vec3<f32> {
-  return log((x + sqrt(((x * x) - 1))));
-}
-
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = tint_acosh(vec3<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_Range_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : f32 = acosh(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_acosh(x : f32) -> f32 {
-  return select(acosh(x), 0.0, (x < 1.0));
-}
-
-fn f() {
-  let v = 1.0;
-  let r : f32 = tint_acosh(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Acosh_Range_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = acosh(vec3<f32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_acosh(x : vec3<f32>) -> vec3<f32> {
-  return select(acosh(x), vec3<f32>(0.0), (x < vec3<f32>(1.0)));
-}
-
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = tint_acosh(vec3<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// asinh
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillSinh() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.asinh = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunAsinh) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  _ = asinh(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillSinh()));
-}
-
-TEST_F(BuiltinPolyfillTest, Asinh_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : f32 = asinh(1.0);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillSinh()));
-}
-
-TEST_F(BuiltinPolyfillTest, Asinh_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : f32 = asinh(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_sinh(x : f32) -> f32 {
-  return log((x + sqrt(((x * x) + 1))));
-}
-
-fn f() {
-  let v = 1.0;
-  let r : f32 = tint_sinh(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillSinh());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Asinh_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = asinh(vec3<f32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_sinh(x : vec3<f32>) -> vec3<f32> {
-  return log((x + sqrt(((x * x) + 1))));
-}
-
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = tint_sinh(vec3<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillSinh());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// atanh
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillAtanh(Level level) {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.atanh = level;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunAtanh) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  _ = atanh(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kNone)));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kRangeCheck)));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : f32 = atanh(0.23);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_Full_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : f32 = atanh(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_atanh(x : f32) -> f32 {
-  return (log(((1 + x) / (1 - x))) * 0.5);
-}
-
-fn f() {
-  let v = 1.0;
-  let r : f32 = tint_atanh(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_Full_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = atanh(vec3<f32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_atanh(x : vec3<f32>) -> vec3<f32> {
-  return (log(((1 + x) / (1 - x))) * 0.5);
-}
-
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = tint_atanh(vec3<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_Range_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : f32 = atanh(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_atanh(x : f32) -> f32 {
-  return select(atanh(x), 0.0, (x >= 1.0));
-}
-
-fn f() {
-  let v = 1.0;
-  let r : f32 = tint_atanh(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kRangeCheck));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Atanh_Range_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = atanh(vec3<f32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_atanh(x : vec3<f32>) -> vec3<f32> {
-  return select(atanh(x), vec3<f32>(0.0), (x >= vec3<f32>(1.0)));
-}
-
-fn f() {
-  let v = 1.0;
-  let r : vec3<f32> = tint_atanh(vec3<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kRangeCheck));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// bgra8unorm
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillBgra8unorm() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.bgra8unorm = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBgra8unorm_StorageTextureVar) {
-    auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_3d<bgra8unorm, write>;
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBgra8unorm()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBgra8unorm_StorageTextureParam) {
-    auto* src = R"(
-fn f(tex : texture_storage_3d<bgra8unorm, write>) {
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBgra8unorm()));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_StorageTextureVar) {
-    auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_3d<bgra8unorm, write>;
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var tex : texture_storage_3d<rgba8unorm, write>;
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_StorageTextureParam) {
-    auto* src = R"(
-fn f(tex : texture_storage_3d<bgra8unorm, write>) {
-}
-)";
-
-    auto* expect = R"(
-fn f(tex : texture_storage_3d<rgba8unorm, write>) {
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureLoad) {
-    auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, read>;
-
-fn f(coords : vec2<i32>) -> vec4<f32> {
-  return textureLoad(tex, coords);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, read>;
-
-fn f(coords : vec2<i32>) -> vec4<f32> {
-  return textureLoad(tex, coords).bgra;
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore) {
-    auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, write>;
-
-fn f(coords : vec2<i32>, value : vec4<f32>) {
-  textureStore(tex, coords, value);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, write>;
-
-fn f(coords : vec2<i32>, value : vec4<f32>) {
-  textureStore(tex, coords, value.bgra);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore_Param) {
-    auto* src = R"(
-fn f(tex : texture_storage_2d<bgra8unorm, write>, coords : vec2<i32>, value : vec4<f32>) {
-  textureStore(tex, coords, value);
-}
-)";
-
-    auto* expect = R"(
-fn f(tex : texture_storage_2d<rgba8unorm, write>, coords : vec2<i32>, value : vec4<f32>) {
-  textureStore(tex, coords, value.bgra);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureStore_WithAtanh) {
-    auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, write>;
-
-fn f(coords : vec2<i32>, value : vec4<f32>) {
-  textureStore(tex, coords, atanh(value));
-}
-)";
-
-    auto* expect = R"(
-fn tint_atanh(x : vec4<f32>) -> vec4<f32> {
-  return (log(((1 + x) / (1 - x))) * 0.5);
-}
-
-@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, write>;
-
-fn f(coords : vec2<i32>, value : vec4<f32>) {
-  textureStore(tex, coords, tint_atanh(value).bgra);
-}
-)";
-
-    BuiltinPolyfill::Builtins builtins;
-    builtins.atanh = BuiltinPolyfill::Level::kFull;
-    builtins.bgra8unorm = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-
-    auto got = Run<BuiltinPolyfill>(src, std::move(data));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Bgra8unorm_TextureLoadAndStore) {
-    auto* src = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<bgra8unorm, read_write>;
-
-fn f(coords : vec2<i32>) {
-  textureStore(tex, coords, textureLoad(tex, coords));
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, read_write>;
-
-fn f(coords : vec2<i32>) {
-  textureStore(tex, coords, textureLoad(tex, coords).bgra.bgra);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBgra8unorm());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// bitshiftModulo
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillBitshiftModulo() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.bitshift_modulo = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shl_scalar) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r = 1i << v;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shl_vector) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r = vec3(1i) << vec3(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shr_scalar) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r = 1i >> v;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shr_vector) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r = vec3(1i) >> vec3(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
-}
-
-TEST_F(BuiltinPolyfillTest, BitshiftModulo_shl_scalar) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r = 1i << v;
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let v = 15u;
-  let r = (1i << (v & 31));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, BitshiftModulo_shl_vector) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r = vec3(1i) << vec3(v);
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let v = 15u;
-  let r = (vec3(1i) << (vec3(v) & vec3<u32>(31)));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, BitshiftModulo_shr_scalar) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r = 1i >> v;
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let v = 15u;
-  let r = (1i >> (v & 31));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, BitshiftModulo_shr_vector) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r = vec3(1i) >> vec3(v);
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let v = 15u;
-  let r = (vec3(1i) >> (vec3(v) & vec3<u32>(31)));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// clampInteger
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillClampInteger() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.clamp_int = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1i;
-  _ = clamp(v, 2i, 3i);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1u;
-  _ = clamp(v, 2u, 3u);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1f;
-  _ = clamp(v, 2f, 3f);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_f16) {
-    auto* src = R"(
-enable f16;
-
-fn f() {
-  let v = 1h;
-  _ = clamp(v, 2h, 3h);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : i32 = clamp(1i, 2i, 3i);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1i;
-  let r : i32 = clamp(v, 2i, 3i);
-}
-)";
-
-    auto* expect = R"(
-fn tint_clamp(e : i32, low : i32, high : i32) -> i32 {
-  return min(max(e, low), high);
-}
-
-fn f() {
-  let v = 1i;
-  let r : i32 = tint_clamp(v, 2i, 3i);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1i;
-  let r : vec3<i32> = clamp(vec3(v), vec3(2i), vec3(3i));
-}
-)";
-
-    auto* expect =
-        R"(
-fn tint_clamp(e : vec3<i32>, low : vec3<i32>, high : vec3<i32>) -> vec3<i32> {
-  return min(max(e, low), high);
-}
-
-fn f() {
-  let v = 1i;
-  let r : vec3<i32> = tint_clamp(vec3(v), vec3(2i), vec3(3i));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_u32) {
-    auto* src = R"(
-fn f() {
-  let r : u32 = clamp(1u, 2u, 3u);
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let r : u32 = clamp(1u, 2u, 3u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ClampInteger_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1u;
-  let r : vec3<u32> = clamp(vec3(v), vec3(2u), vec3(3u));
-}
-)";
-
-    auto* expect =
-        R"(
-fn tint_clamp(e : vec3<u32>, low : vec3<u32>, high : vec3<u32>) -> vec3<u32> {
-  return min(max(e, low), high);
-}
-
-fn f() {
-  let v = 1u;
-  let r : vec3<u32> = tint_clamp(vec3(v), vec3(2u), vec3(3u));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// conv_f32_to_iu32
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillConvF32ToIU32() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.conv_f32_to_iu32 = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunConvF32ToI32) {
-    auto* src = R"(
-fn f() {
-  let f = 42.0;
-  _ = i32(f);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunConvF32ToU32) {
-    auto* src = R"(
-fn f() {
-  let f = 42.0;
-  _ = u32(f);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunConvVec3F32ToVec3I32) {
-    auto* src = R"(
-fn f() {
-  let f = vec3(42.0);
-  _ = vec3<i32>(f);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunConvVec3F32ToVec3U32) {
-    auto* src = R"(
-fn f() {
-  let f = vec3(42.0);
-  _ = vec3<u32>(f);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ConvF32ToI32) {
-    auto* src = R"(
-fn f() {
-  let f = 42.0;
-  _ = i32(f);
-}
-)";
-    auto* expect = R"(
-fn tint_ftoi(v : f32) -> i32 {
-  return select(2147483647, select(i32(v), -2147483648, (v < -2147483648.0)), (v <= 2147483520.0));
-}
-
-fn f() {
-  let f = 42.0;
-  _ = tint_ftoi(f);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ConvF32ToU32) {
-    auto* src = R"(
-fn f() {
-  let f = 42.0;
-  _ = u32(f);
-}
-)";
-    auto* expect = R"(
-fn tint_ftou(v : f32) -> u32 {
-  return select(4294967295, select(u32(v), 0, (v < 0.0)), (v <= 4294967040.0));
-}
-
-fn f() {
-  let f = 42.0;
-  _ = tint_ftou(f);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ConvVec3F32ToVec3I32) {
-    auto* src = R"(
-fn f() {
-  let f = vec3(42.0);
-  _ = vec3<i32>(f);
-}
-)";
-    auto* expect = R"(
-fn tint_ftoi(v : vec3<f32>) -> vec3<i32> {
-  return select(vec3(2147483647), select(vec3<i32>(v), vec3(-2147483648), (v < vec3(-2147483648.0))), (v <= vec3(2147483520.0)));
-}
-
-fn f() {
-  let f = vec3(42.0);
-  _ = tint_ftoi(f);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ConvVec3F32ToVec3U32) {
-    auto* src = R"(
-fn f() {
-  let f = vec3(42.0);
-  _ = vec3<u32>(f);
-}
-)";
-    auto* expect = R"(
-fn tint_ftou(v : vec3<f32>) -> vec3<u32> {
-  return select(vec3(4294967295), select(vec3<u32>(v), vec3(0), (v < vec3(0.0))), (v <= vec3(4294967040.0)));
-}
-
-fn f() {
-  let f = vec3(42.0);
-  _ = tint_ftou(f);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// countLeadingZeros
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillCountLeadingZeros() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.count_leading_zeros = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunCountLeadingZeros) {
-    auto* src = R"(
-fn f() {
-  let v = 15;
-  _ = countLeadingZeros(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillCountLeadingZeros()));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : i32 = countLeadingZeros(15i);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillCountLeadingZeros()));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  let r : i32 = countLeadingZeros(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_count_leading_zeros(v : i32) -> i32 {
-  var x = u32(v);
-  let b16 = select(0u, 16u, (x <= 65535u));
-  x = (x << b16);
-  let b8 = select(0u, 8u, (x <= 16777215u));
-  x = (x << b8);
-  let b4 = select(0u, 4u, (x <= 268435455u));
-  x = (x << b4);
-  let b2 = select(0u, 2u, (x <= 1073741823u));
-  x = (x << b2);
-  let b1 = select(0u, 1u, (x <= 2147483647u));
-  let is_zero = select(0u, 1u, (x == 0u));
-  return i32((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
-  let v = 15i;
-  let r : i32 = tint_count_leading_zeros(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillCountLeadingZeros());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r : u32 = countLeadingZeros(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_count_leading_zeros(v : u32) -> u32 {
-  var x = u32(v);
-  let b16 = select(0u, 16u, (x <= 65535u));
-  x = (x << b16);
-  let b8 = select(0u, 8u, (x <= 16777215u));
-  x = (x << b8);
-  let b4 = select(0u, 4u, (x <= 268435455u));
-  x = (x << b4);
-  let b2 = select(0u, 2u, (x <= 1073741823u));
-  x = (x << b2);
-  let b1 = select(0u, 1u, (x <= 2147483647u));
-  let is_zero = select(0u, 1u, (x == 0u));
-  return u32((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
-  let v = 15u;
-  let r : u32 = tint_count_leading_zeros(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillCountLeadingZeros());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  let r : vec3<i32> = countLeadingZeros(vec3<i32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_count_leading_zeros(v : vec3<i32>) -> vec3<i32> {
-  var x = vec3<u32>(v);
-  let b16 = select(vec3<u32>(0u), vec3<u32>(16u), (x <= vec3<u32>(65535u)));
-  x = (x << b16);
-  let b8 = select(vec3<u32>(0u), vec3<u32>(8u), (x <= vec3<u32>(16777215u)));
-  x = (x << b8);
-  let b4 = select(vec3<u32>(0u), vec3<u32>(4u), (x <= vec3<u32>(268435455u)));
-  x = (x << b4);
-  let b2 = select(vec3<u32>(0u), vec3<u32>(2u), (x <= vec3<u32>(1073741823u)));
-  x = (x << b2);
-  let b1 = select(vec3<u32>(0u), vec3<u32>(1u), (x <= vec3<u32>(2147483647u)));
-  let is_zero = select(vec3<u32>(0u), vec3<u32>(1u), (x == vec3<u32>(0u)));
-  return vec3<i32>((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
-  let v = 15i;
-  let r : vec3<i32> = tint_count_leading_zeros(vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillCountLeadingZeros());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountLeadingZeros_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r : vec3<u32> = countLeadingZeros(vec3<u32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_count_leading_zeros(v : vec3<u32>) -> vec3<u32> {
-  var x = vec3<u32>(v);
-  let b16 = select(vec3<u32>(0u), vec3<u32>(16u), (x <= vec3<u32>(65535u)));
-  x = (x << b16);
-  let b8 = select(vec3<u32>(0u), vec3<u32>(8u), (x <= vec3<u32>(16777215u)));
-  x = (x << b8);
-  let b4 = select(vec3<u32>(0u), vec3<u32>(4u), (x <= vec3<u32>(268435455u)));
-  x = (x << b4);
-  let b2 = select(vec3<u32>(0u), vec3<u32>(2u), (x <= vec3<u32>(1073741823u)));
-  x = (x << b2);
-  let b1 = select(vec3<u32>(0u), vec3<u32>(1u), (x <= vec3<u32>(2147483647u)));
-  let is_zero = select(vec3<u32>(0u), vec3<u32>(1u), (x == vec3<u32>(0u)));
-  return vec3<u32>((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
-  let v = 15u;
-  let r : vec3<u32> = tint_count_leading_zeros(vec3<u32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillCountLeadingZeros());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// countTrailingZeros
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillCountTrailingZeros() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.count_trailing_zeros = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunCountTrailingZeros) {
-    auto* src = R"(
-fn f() {
-  let v = 15;
-  _ = countTrailingZeros(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillCountTrailingZeros()));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : i32 = countTrailingZeros(15i);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillCountTrailingZeros()));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  let r : i32 = countTrailingZeros(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_count_trailing_zeros(v : i32) -> i32 {
-  var x = u32(v);
-  let b16 = select(16u, 0u, bool((x & 65535u)));
-  x = (x >> b16);
-  let b8 = select(8u, 0u, bool((x & 255u)));
-  x = (x >> b8);
-  let b4 = select(4u, 0u, bool((x & 15u)));
-  x = (x >> b4);
-  let b2 = select(2u, 0u, bool((x & 3u)));
-  x = (x >> b2);
-  let b1 = select(1u, 0u, bool((x & 1u)));
-  let is_zero = select(0u, 1u, (x == 0u));
-  return i32((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
-  let v = 15i;
-  let r : i32 = tint_count_trailing_zeros(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillCountTrailingZeros());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r : u32 = countTrailingZeros(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_count_trailing_zeros(v : u32) -> u32 {
-  var x = u32(v);
-  let b16 = select(16u, 0u, bool((x & 65535u)));
-  x = (x >> b16);
-  let b8 = select(8u, 0u, bool((x & 255u)));
-  x = (x >> b8);
-  let b4 = select(4u, 0u, bool((x & 15u)));
-  x = (x >> b4);
-  let b2 = select(2u, 0u, bool((x & 3u)));
-  x = (x >> b2);
-  let b1 = select(1u, 0u, bool((x & 1u)));
-  let is_zero = select(0u, 1u, (x == 0u));
-  return u32((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
-  let v = 15u;
-  let r : u32 = tint_count_trailing_zeros(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillCountTrailingZeros());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  let r : vec3<i32> = countTrailingZeros(vec3<i32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_count_trailing_zeros(v : vec3<i32>) -> vec3<i32> {
-  var x = vec3<u32>(v);
-  let b16 = select(vec3<u32>(16u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(65535u))));
-  x = (x >> b16);
-  let b8 = select(vec3<u32>(8u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(255u))));
-  x = (x >> b8);
-  let b4 = select(vec3<u32>(4u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(15u))));
-  x = (x >> b4);
-  let b2 = select(vec3<u32>(2u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(3u))));
-  x = (x >> b2);
-  let b1 = select(vec3<u32>(1u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(1u))));
-  let is_zero = select(vec3<u32>(0u), vec3<u32>(1u), (x == vec3<u32>(0u)));
-  return vec3<i32>((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
-  let v = 15i;
-  let r : vec3<i32> = tint_count_trailing_zeros(vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillCountTrailingZeros());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, CountTrailingZeros_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r : vec3<u32> = countTrailingZeros(vec3<u32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_count_trailing_zeros(v : vec3<u32>) -> vec3<u32> {
-  var x = vec3<u32>(v);
-  let b16 = select(vec3<u32>(16u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(65535u))));
-  x = (x >> b16);
-  let b8 = select(vec3<u32>(8u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(255u))));
-  x = (x >> b8);
-  let b4 = select(vec3<u32>(4u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(15u))));
-  x = (x >> b4);
-  let b2 = select(vec3<u32>(2u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(3u))));
-  x = (x >> b2);
-  let b1 = select(vec3<u32>(1u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(1u))));
-  let is_zero = select(vec3<u32>(0u), vec3<u32>(1u), (x == vec3<u32>(0u)));
-  return vec3<u32>((((((b16 | b8) | b4) | b2) | b1) + is_zero));
-}
-
-fn f() {
-  let v = 15u;
-  let r : vec3<u32> = tint_count_trailing_zeros(vec3<u32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillCountTrailingZeros());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// extractBits
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillExtractBits(Level level) {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.extract_bits = level;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunExtractBits) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  _ = extractBits(v, 5u, 6u);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillExtractBits(Level::kNone)));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters)));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : i32 = countTrailingZeros(15i);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Full_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  let r : i32 = extractBits(v, 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_extract_bits(v : i32, offset : u32, count : u32) -> i32 {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  let shl = (32u - e);
-  let shr = (shl + s);
-  let shl_result = select(i32(), (v << shl), (shl < 32u));
-  return select(((shl_result >> 31u) >> 1u), (shl_result >> shr), (shr < 32u));
-}
-
-fn f() {
-  let v = 1234i;
-  let r : i32 = tint_extract_bits(v, 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Full_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234u;
-  let r : u32 = extractBits(v, 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_extract_bits(v : u32, offset : u32, count : u32) -> u32 {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  let shl = (32u - e);
-  let shr = (shl + s);
-  let shl_result = select(u32(), (v << shl), (shl < 32u));
-  return select(((shl_result >> 31u) >> 1u), (shl_result >> shr), (shr < 32u));
-}
-
-fn f() {
-  let v = 1234u;
-  let r : u32 = tint_extract_bits(v, 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Full_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  let r : vec3<i32> = extractBits(vec3<i32>(v), 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_extract_bits(v : vec3<i32>, offset : u32, count : u32) -> vec3<i32> {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  let shl = (32u - e);
-  let shr = (shl + s);
-  let shl_result = select(vec3<i32>(), (v << vec3<u32>(shl)), (shl < 32u));
-  return select(((shl_result >> vec3<u32>(31u)) >> vec3<u32>(1u)), (shl_result >> vec3<u32>(shr)), (shr < 32u));
-}
-
-fn f() {
-  let v = 1234i;
-  let r : vec3<i32> = tint_extract_bits(vec3<i32>(v), 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Full_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234u;
-  let r : vec3<u32> = extractBits(vec3<u32>(v), 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_extract_bits(v : vec3<u32>, offset : u32, count : u32) -> vec3<u32> {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  let shl = (32u - e);
-  let shr = (shl + s);
-  let shl_result = select(vec3<u32>(), (v << vec3<u32>(shl)), (shl < 32u));
-  return select(((shl_result >> vec3<u32>(31u)) >> vec3<u32>(1u)), (shl_result >> vec3<u32>(shr)), (shr < 32u));
-}
-
-fn f() {
-  let v = 1234u;
-  let r : vec3<u32> = tint_extract_bits(vec3<u32>(v), 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Clamp_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  let r : i32 = extractBits(v, 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_extract_bits(v : i32, offset : u32, count : u32) -> i32 {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  return extractBits(v, s, (e - s));
-}
-
-fn f() {
-  let v = 1234i;
-  let r : i32 = tint_extract_bits(v, 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Clamp_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234u;
-  let r : u32 = extractBits(v, 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_extract_bits(v : u32, offset : u32, count : u32) -> u32 {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  return extractBits(v, s, (e - s));
-}
-
-fn f() {
-  let v = 1234u;
-  let r : u32 = tint_extract_bits(v, 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Clamp_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  let r : vec3<i32> = extractBits(vec3<i32>(v), 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_extract_bits(v : vec3<i32>, offset : u32, count : u32) -> vec3<i32> {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  return extractBits(v, s, (e - s));
-}
-
-fn f() {
-  let v = 1234i;
-  let r : vec3<i32> = tint_extract_bits(vec3<i32>(v), 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, ExtractBits_Clamp_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234u;
-  let r : vec3<u32> = extractBits(vec3<u32>(v), 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_extract_bits(v : vec3<u32>, offset : u32, count : u32) -> vec3<u32> {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  return extractBits(v, s, (e - s));
-}
-
-fn f() {
-  let v = 1234u;
-  let r : vec3<u32> = tint_extract_bits(vec3<u32>(v), 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillExtractBits(Level::kClampParameters));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// firstLeadingBit
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillFirstLeadingBit() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.first_leading_bit = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunFirstLeadingBit) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  _ = firstLeadingBit(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstLeadingBit()));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : i32 = firstLeadingBit(15i);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstLeadingBit()));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  let r : i32 = firstLeadingBit(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_first_leading_bit(v : i32) -> i32 {
-  var x = select(u32(v), u32(~(v)), (v < 0i));
-  let b16 = select(0u, 16u, bool((x & 4294901760u)));
-  x = (x >> b16);
-  let b8 = select(0u, 8u, bool((x & 65280u)));
-  x = (x >> b8);
-  let b4 = select(0u, 4u, bool((x & 240u)));
-  x = (x >> b4);
-  let b2 = select(0u, 2u, bool((x & 12u)));
-  x = (x >> b2);
-  let b1 = select(0u, 1u, bool((x & 2u)));
-  let is_zero = select(0u, 4294967295u, (x == 0u));
-  return i32((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
-  let v = 15i;
-  let r : i32 = tint_first_leading_bit(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFirstLeadingBit());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r : u32 = firstLeadingBit(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_first_leading_bit(v : u32) -> u32 {
-  var x = v;
-  let b16 = select(0u, 16u, bool((x & 4294901760u)));
-  x = (x >> b16);
-  let b8 = select(0u, 8u, bool((x & 65280u)));
-  x = (x >> b8);
-  let b4 = select(0u, 4u, bool((x & 240u)));
-  x = (x >> b4);
-  let b2 = select(0u, 2u, bool((x & 12u)));
-  x = (x >> b2);
-  let b1 = select(0u, 1u, bool((x & 2u)));
-  let is_zero = select(0u, 4294967295u, (x == 0u));
-  return u32((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
-  let v = 15u;
-  let r : u32 = tint_first_leading_bit(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFirstLeadingBit());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  let r : vec3<i32> = firstLeadingBit(vec3<i32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_first_leading_bit(v : vec3<i32>) -> vec3<i32> {
-  var x = select(vec3<u32>(v), vec3<u32>(~(v)), (v < vec3<i32>(0i)));
-  let b16 = select(vec3<u32>(0u), vec3<u32>(16u), vec3<bool>((x & vec3<u32>(4294901760u))));
-  x = (x >> b16);
-  let b8 = select(vec3<u32>(0u), vec3<u32>(8u), vec3<bool>((x & vec3<u32>(65280u))));
-  x = (x >> b8);
-  let b4 = select(vec3<u32>(0u), vec3<u32>(4u), vec3<bool>((x & vec3<u32>(240u))));
-  x = (x >> b4);
-  let b2 = select(vec3<u32>(0u), vec3<u32>(2u), vec3<bool>((x & vec3<u32>(12u))));
-  x = (x >> b2);
-  let b1 = select(vec3<u32>(0u), vec3<u32>(1u), vec3<bool>((x & vec3<u32>(2u))));
-  let is_zero = select(vec3<u32>(0u), vec3<u32>(4294967295u), (x == vec3<u32>(0u)));
-  return vec3<i32>((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
-  let v = 15i;
-  let r : vec3<i32> = tint_first_leading_bit(vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFirstLeadingBit());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstLeadingBit_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r : vec3<u32> = firstLeadingBit(vec3<u32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_first_leading_bit(v : vec3<u32>) -> vec3<u32> {
-  var x = v;
-  let b16 = select(vec3<u32>(0u), vec3<u32>(16u), vec3<bool>((x & vec3<u32>(4294901760u))));
-  x = (x >> b16);
-  let b8 = select(vec3<u32>(0u), vec3<u32>(8u), vec3<bool>((x & vec3<u32>(65280u))));
-  x = (x >> b8);
-  let b4 = select(vec3<u32>(0u), vec3<u32>(4u), vec3<bool>((x & vec3<u32>(240u))));
-  x = (x >> b4);
-  let b2 = select(vec3<u32>(0u), vec3<u32>(2u), vec3<bool>((x & vec3<u32>(12u))));
-  x = (x >> b2);
-  let b1 = select(vec3<u32>(0u), vec3<u32>(1u), vec3<bool>((x & vec3<u32>(2u))));
-  let is_zero = select(vec3<u32>(0u), vec3<u32>(4294967295u), (x == vec3<u32>(0u)));
-  return vec3<u32>((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
-  let v = 15u;
-  let r : vec3<u32> = tint_first_leading_bit(vec3<u32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFirstLeadingBit());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// firstTrailingBit
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillFirstTrailingBit() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.first_trailing_bit = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunFirstTrailingBit) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  _ = firstTrailingBit(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstTrailingBit()));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : i32 = firstTrailingBit(15i);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillFirstTrailingBit()));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  let r : i32 = firstTrailingBit(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_first_trailing_bit(v : i32) -> i32 {
-  var x = u32(v);
-  let b16 = select(16u, 0u, bool((x & 65535u)));
-  x = (x >> b16);
-  let b8 = select(8u, 0u, bool((x & 255u)));
-  x = (x >> b8);
-  let b4 = select(4u, 0u, bool((x & 15u)));
-  x = (x >> b4);
-  let b2 = select(2u, 0u, bool((x & 3u)));
-  x = (x >> b2);
-  let b1 = select(1u, 0u, bool((x & 1u)));
-  let is_zero = select(0u, 4294967295u, (x == 0u));
-  return i32((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
-  let v = 15i;
-  let r : i32 = tint_first_trailing_bit(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFirstTrailingBit());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r : u32 = firstTrailingBit(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_first_trailing_bit(v : u32) -> u32 {
-  var x = u32(v);
-  let b16 = select(16u, 0u, bool((x & 65535u)));
-  x = (x >> b16);
-  let b8 = select(8u, 0u, bool((x & 255u)));
-  x = (x >> b8);
-  let b4 = select(4u, 0u, bool((x & 15u)));
-  x = (x >> b4);
-  let b2 = select(2u, 0u, bool((x & 3u)));
-  x = (x >> b2);
-  let b1 = select(1u, 0u, bool((x & 1u)));
-  let is_zero = select(0u, 4294967295u, (x == 0u));
-  return u32((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
-  let v = 15u;
-  let r : u32 = tint_first_trailing_bit(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFirstTrailingBit());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 15i;
-  let r : vec3<i32> = firstTrailingBit(vec3<i32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_first_trailing_bit(v : vec3<i32>) -> vec3<i32> {
-  var x = vec3<u32>(v);
-  let b16 = select(vec3<u32>(16u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(65535u))));
-  x = (x >> b16);
-  let b8 = select(vec3<u32>(8u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(255u))));
-  x = (x >> b8);
-  let b4 = select(vec3<u32>(4u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(15u))));
-  x = (x >> b4);
-  let b2 = select(vec3<u32>(2u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(3u))));
-  x = (x >> b2);
-  let b1 = select(vec3<u32>(1u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(1u))));
-  let is_zero = select(vec3<u32>(0u), vec3<u32>(4294967295u), (x == vec3<u32>(0u)));
-  return vec3<i32>((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
-  let v = 15i;
-  let r : vec3<i32> = tint_first_trailing_bit(vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFirstTrailingBit());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FirstTrailingBit_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 15u;
-  let r : vec3<u32> = firstTrailingBit(vec3<u32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_first_trailing_bit(v : vec3<u32>) -> vec3<u32> {
-  var x = vec3<u32>(v);
-  let b16 = select(vec3<u32>(16u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(65535u))));
-  x = (x >> b16);
-  let b8 = select(vec3<u32>(8u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(255u))));
-  x = (x >> b8);
-  let b4 = select(vec3<u32>(4u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(15u))));
-  x = (x >> b4);
-  let b2 = select(vec3<u32>(2u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(3u))));
-  x = (x >> b2);
-  let b1 = select(vec3<u32>(1u), vec3<u32>(0u), vec3<bool>((x & vec3<u32>(1u))));
-  let is_zero = select(vec3<u32>(0u), vec3<u32>(4294967295u), (x == vec3<u32>(0u)));
-  return vec3<u32>((((((b16 | b8) | b4) | b2) | b1) | is_zero));
-}
-
-fn f() {
-  let v = 15u;
-  let r : vec3<u32> = tint_first_trailing_bit(vec3<u32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFirstTrailingBit());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// fwidthFine
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillFwidthFine() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.fwidth_fine = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunFwidthFine) {
-    auto* src = R"(
-fn f() {
-  let v = 0.5f;
-  _ = fwidthFine(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillFwidthFine()));
-}
-
-TEST_F(BuiltinPolyfillTest, FwidthFine_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 0.5f;
-  let r : f32 = fwidthFine(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_fwidth_fine(v : f32) -> f32 {
-  return (abs(dpdxFine(v)) + abs(dpdyFine(v)));
-}
-
-fn f() {
-  let v = 0.5f;
-  let r : f32 = tint_fwidth_fine(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFwidthFine());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, FwidthFine_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 0.5f;
-  let r : vec3<f32> = fwidthFine(vec3<f32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_fwidth_fine(v : vec3<f32>) -> vec3<f32> {
-  return (abs(dpdxFine(v)) + abs(dpdyFine(v)));
-}
-
-fn f() {
-  let v = 0.5f;
-  let r : vec3<f32> = tint_fwidth_fine(vec3<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillFwidthFine());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// insertBits
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillInsertBits(Level level) {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.insert_bits = level;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunInsertBits) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  _ = insertBits(v, 5678, 5u, 6u);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kNone)));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters)));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : i32 = insertBits(1234i, 5678i, 5u, 6u);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull)));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Full_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  let r : i32 = insertBits(v, 5678, 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_insert_bits(v : i32, n : i32, offset : u32, count : u32) -> i32 {
-  let e = (offset + count);
-  let mask = ((select(0u, (1u << offset), (offset < 32u)) - 1u) ^ (select(0u, (1u << e), (e < 32u)) - 1u));
-  return ((select(i32(), (n << offset), (offset < 32u)) & i32(mask)) | (v & i32(~(mask))));
-}
-
-fn f() {
-  let v = 1234i;
-  let r : i32 = tint_insert_bits(v, 5678, 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Full_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234u;
-  let r : u32 = insertBits(v, 5678u, 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_insert_bits(v : u32, n : u32, offset : u32, count : u32) -> u32 {
-  let e = (offset + count);
-  let mask = ((select(0u, (1u << offset), (offset < 32u)) - 1u) ^ (select(0u, (1u << e), (e < 32u)) - 1u));
-  return ((select(u32(), (n << offset), (offset < 32u)) & mask) | (v & ~(mask)));
-}
-
-fn f() {
-  let v = 1234u;
-  let r : u32 = tint_insert_bits(v, 5678u, 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Full_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  let r : vec3<i32> = insertBits(vec3<i32>(v), vec3<i32>(5678), 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_insert_bits(v : vec3<i32>, n : vec3<i32>, offset : u32, count : u32) -> vec3<i32> {
-  let e = (offset + count);
-  let mask = ((select(0u, (1u << offset), (offset < 32u)) - 1u) ^ (select(0u, (1u << e), (e < 32u)) - 1u));
-  return ((select(vec3<i32>(), (n << vec3<u32>(offset)), (offset < 32u)) & vec3<i32>(i32(mask))) | (v & vec3<i32>(i32(~(mask)))));
-}
-
-fn f() {
-  let v = 1234i;
-  let r : vec3<i32> = tint_insert_bits(vec3<i32>(v), vec3<i32>(5678), 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Full_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234u;
-  let r : vec3<u32> = insertBits(vec3<u32>(v), vec3<u32>(5678u), 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_insert_bits(v : vec3<u32>, n : vec3<u32>, offset : u32, count : u32) -> vec3<u32> {
-  let e = (offset + count);
-  let mask = ((select(0u, (1u << offset), (offset < 32u)) - 1u) ^ (select(0u, (1u << e), (e < 32u)) - 1u));
-  return ((select(vec3<u32>(), (n << vec3<u32>(offset)), (offset < 32u)) & vec3<u32>(mask)) | (v & vec3<u32>(~(mask))));
-}
-
-fn f() {
-  let v = 1234u;
-  let r : vec3<u32> = tint_insert_bits(vec3<u32>(v), vec3<u32>(5678u), 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kFull));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Clamp_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  let r : i32 = insertBits(v, 5678, 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_insert_bits(v : i32, n : i32, offset : u32, count : u32) -> i32 {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  return insertBits(v, n, s, (e - s));
-}
-
-fn f() {
-  let v = 1234i;
-  let r : i32 = tint_insert_bits(v, 5678, 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Clamp_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234u;
-  let r : u32 = insertBits(v, 5678u, 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_insert_bits(v : u32, n : u32, offset : u32, count : u32) -> u32 {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  return insertBits(v, n, s, (e - s));
-}
-
-fn f() {
-  let v = 1234u;
-  let r : u32 = tint_insert_bits(v, 5678u, 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Clamp_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234i;
-  let r : vec3<i32> = insertBits(vec3<i32>(v), vec3<i32>(5678), 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_insert_bits(v : vec3<i32>, n : vec3<i32>, offset : u32, count : u32) -> vec3<i32> {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  return insertBits(v, n, s, (e - s));
-}
-
-fn f() {
-  let v = 1234i;
-  let r : vec3<i32> = tint_insert_bits(vec3<i32>(v), vec3<i32>(5678), 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, InsertBits_Clamp_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 1234u;
-  let r : vec3<u32> = insertBits(vec3<u32>(v), vec3<u32>(5678u), 5u, 6u);
-}
-)";
-
-    auto* expect = R"(
-fn tint_insert_bits(v : vec3<u32>, n : vec3<u32>, offset : u32, count : u32) -> vec3<u32> {
-  let s = min(offset, 32u);
-  let e = min(32u, (s + count));
-  return insertBits(v, n, s, (e - s));
-}
-
-fn f() {
-  let v = 1234u;
-  let r : vec3<u32> = tint_insert_bits(vec3<u32>(v), vec3<u32>(5678u), 5u, 6u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillInsertBits(Level::kClampParameters));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// precise_float_mod
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillPreciseFloatMod() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.precise_float_mod = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunPreciseFloatMod) {
-    auto* src = R"(
-fn f() {
-  let v = 10f;
-  let x = 20f % v;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillPreciseFloatMod()));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_af_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 10f;
-  let x = 20.0 % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
-  return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
-  let v = 10.0f;
-  let x = tint_float_mod(20.0, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_f32_af) {
-    auto* src = R"(
-fn f() {
-  let v = 10.0;
-  let x = 20f % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
-  return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
-  let v = 10.0;
-  let x = tint_float_mod(20.0f, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_f32_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 10f;
-  let x = 20f % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
-  return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
-  let v = 10.0f;
-  let x = tint_float_mod(20.0f, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_Overloads) {
-    auto* src = R"(
-fn f() {
-  let v = 10f;
-  let x = 20f % v;
-  let w = 10i;
-  let y = 20i % w;
-  let u = 10u;
-  let z = 20u % u;
-}
-)";
-
-    auto* expect = R"(
-fn tint_float_mod(lhs : f32, rhs : f32) -> f32 {
-  return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
-  let v = 10.0f;
-  let x = tint_float_mod(20.0f, v);
-  let w = 10i;
-  let y = (20i % w);
-  let u = 10u;
-  let z = (20u % u);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_af_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 10f;
-  let x = vec3(20.0) % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
-  let r = vec3<f32>(rhs);
-  return (lhs - (trunc((lhs / r)) * r));
-}
-
-fn f() {
-  let v = 10.0f;
-  let x = tint_float_mod(vec3(20.0), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_af) {
-    auto* src = R"(
-fn f() {
-  let v = 10.0;
-  let x = vec3(20f) % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
-  let r = vec3<f32>(rhs);
-  return (lhs - (trunc((lhs / r)) * r));
-}
-
-fn f() {
-  let v = 10.0;
-  let x = tint_float_mod(vec3(20.0f), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 10f;
-  let x = vec3(20f) % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_float_mod(lhs : vec3<f32>, rhs : f32) -> vec3<f32> {
-  let r = vec3<f32>(rhs);
-  return (lhs - (trunc((lhs / r)) * r));
-}
-
-fn f() {
-  let v = 10.0f;
-  let x = tint_float_mod(vec3(20.0f), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, PreciseFloatMod_vec3_f32_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 10f;
-  let x = vec3<f32>(20f) % vec3<f32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_float_mod(lhs : vec3<f32>, rhs : vec3<f32>) -> vec3<f32> {
-  return (lhs - (trunc((lhs / rhs)) * rhs));
-}
-
-fn f() {
-  let v = 10.0f;
-  let x = tint_float_mod(vec3<f32>(20.0f), vec3<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPreciseFloatMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// int_div_mod
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillIntDivMod() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.int_div_mod = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunIntDiv) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20i / v;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillIntDivMod()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunIntMod) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20i % v;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillIntDivMod()));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_ai_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20 / v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : i32, rhs : i32) -> i32 {
-  return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(20, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_ai_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20 % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : i32) -> i32 {
-  let rhs_or_one = select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1))));
-  if (any(((u32((lhs | rhs_or_one)) & 2147483648u) != 0u))) {
-    return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
-  } else {
-    return (lhs % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(20, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_i32_ai) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = v / 20;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : i32, rhs : i32) -> i32 {
-  return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(v, 20);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_i32_ai) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = v % 20;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : i32) -> i32 {
-  let rhs_or_one = select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1))));
-  if (any(((u32((lhs | rhs_or_one)) & 2147483648u) != 0u))) {
-    return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
-  } else {
-    return (lhs % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(v, 20);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_i32_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20i / v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : i32, rhs : i32) -> i32 {
-  return (lhs / select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1)))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(20i, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_i32_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20i % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : i32) -> i32 {
-  let rhs_or_one = select(rhs, 1, ((rhs == 0) | ((lhs == -2147483648) & (rhs == -1))));
-  if (any(((u32((lhs | rhs_or_one)) & 2147483648u) != 0u))) {
-    return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
-  } else {
-    return (lhs % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(20i, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_ai_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = 20 / v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : u32, rhs : u32) -> u32 {
-  return (lhs / select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_div(20, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_ai_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = 20 % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : u32) -> u32 {
-  return (lhs % select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_mod(20, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_u32_ai) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = v / 20;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : u32, rhs : u32) -> u32 {
-  return (lhs / select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_div(v, 20);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_u32_ai) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = v % 20;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : u32) -> u32 {
-  return (lhs % select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_mod(v, 20);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_u32_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = 20u / v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : u32, rhs : u32) -> u32 {
-  return (lhs / select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_div(20u, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_u32_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = 20u % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : u32) -> u32 {
-  return (lhs % select(rhs, 1, (rhs == 0)));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_mod(20u, v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_ai_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = vec3(20) / v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
-  let r = vec3<i32>(rhs);
-  return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(vec3(20), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_ai_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = vec3(20) % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
-  let r = vec3<i32>(rhs);
-  let rhs_or_one = select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1)))));
-  if (any(((vec3<u32>((lhs | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
-    return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
-  } else {
-    return (lhs % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(vec3(20), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_ai) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = vec3(v) / 20;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
-  let r = vec3<i32>(rhs);
-  return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(vec3(v), 20);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_ai) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = vec3(v) % 20;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
-  let r = vec3<i32>(rhs);
-  let rhs_or_one = select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1)))));
-  if (any(((vec3<u32>((lhs | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
-    return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
-  } else {
-    return (lhs % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(vec3(v), 20);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = vec3<i32>(20i) / v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
-  let r = vec3<i32>(rhs);
-  return (lhs / select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1))))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(vec3<i32>(20i), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = vec3<i32>(20i) % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : vec3<i32>, rhs : i32) -> vec3<i32> {
-  let r = vec3<i32>(rhs);
-  let rhs_or_one = select(r, vec3(1), ((r == vec3(0)) | ((lhs == vec3(-2147483648)) & (r == vec3(-1)))));
-  if (any(((vec3<u32>((lhs | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
-    return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
-  } else {
-    return (lhs % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(vec3<i32>(20i), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_u32_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = vec3<u32>(20u) / v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : vec3<u32>, rhs : u32) -> vec3<u32> {
-  let r = vec3<u32>(rhs);
-  return (lhs / select(r, vec3(1), (r == vec3(0))));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_div(vec3<u32>(20u), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_u32_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = vec3<u32>(20u) % v;
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : vec3<u32>, rhs : u32) -> vec3<u32> {
-  let r = vec3<u32>(rhs);
-  return (lhs % select(r, vec3(1), (r == vec3(0))));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_mod(vec3<u32>(20u), v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_ai_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20 / vec3(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
-  let l = vec3<i32>(lhs);
-  return (l / select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1))))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(20, vec3(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_ai_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20 % vec3(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
-  let l = vec3<i32>(lhs);
-  let rhs_or_one = select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1)))));
-  if (any(((vec3<u32>((l | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
-    return (l - ((l / rhs_or_one) * rhs_or_one));
-  } else {
-    return (l % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(20, vec3(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_i32_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20i / vec3<i32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
-  let l = vec3<i32>(lhs);
-  return (l / select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1))))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(20i, vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_i32_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = 20i % vec3<i32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : i32, rhs : vec3<i32>) -> vec3<i32> {
-  let l = vec3<i32>(lhs);
-  let rhs_or_one = select(rhs, vec3(1), ((rhs == vec3(0)) | ((l == vec3(-2147483648)) & (rhs == vec3(-1)))));
-  if (any(((vec3<u32>((l | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
-    return (l - ((l / rhs_or_one) * rhs_or_one));
-  } else {
-    return (l % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(20i, vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_u32_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = 20u / vec3<u32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : u32, rhs : vec3<u32>) -> vec3<u32> {
-  let l = vec3<u32>(lhs);
-  return (l / select(rhs, vec3(1), (rhs == vec3(0))));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_div(20u, vec3<u32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_u32_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = 20u % vec3<u32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : vec3<u32>) -> vec3<u32> {
-  let l = vec3<u32>(lhs);
-  return (l % select(rhs, vec3(1), (rhs == vec3(0))));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_mod(20u, vec3<u32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_i32_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = vec3<i32>(20i) / vec3<i32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : vec3<i32>, rhs : vec3<i32>) -> vec3<i32> {
-  return (lhs / select(rhs, vec3(1), ((rhs == vec3(0)) | ((lhs == vec3(-2147483648)) & (rhs == vec3(-1))))));
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_div(vec3<i32>(20i), vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_i32_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 10i;
-  let x = vec3<i32>(20i) % vec3<i32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : vec3<i32>, rhs : vec3<i32>) -> vec3<i32> {
-  let rhs_or_one = select(rhs, vec3(1), ((rhs == vec3(0)) | ((lhs == vec3(-2147483648)) & (rhs == vec3(-1)))));
-  if (any(((vec3<u32>((lhs | rhs_or_one)) & vec3<u32>(2147483648u)) != vec3<u32>(0u)))) {
-    return (lhs - ((lhs / rhs_or_one) * rhs_or_one));
-  } else {
-    return (lhs % rhs_or_one);
-  }
-}
-
-fn f() {
-  let v = 10i;
-  let x = tint_mod(vec3<i32>(20i), vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntDiv_vec3_u32_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = vec3<u32>(20u) / vec3<u32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_div(lhs : vec3<u32>, rhs : vec3<u32>) -> vec3<u32> {
-  return (lhs / select(rhs, vec3(1), (rhs == vec3(0))));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_div(vec3<u32>(20u), vec3<u32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, IntMod_vec3_u32_vec3_u32) {
-    auto* src = R"(
-fn f() {
-  let v = 10u;
-  let x = vec3<u32>(20u) % vec3<u32>(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : vec3<u32>, rhs : vec3<u32>) -> vec3<u32> {
-  return (lhs % select(rhs, vec3(1), (rhs == vec3(0))));
-}
-
-fn f() {
-  let v = 10u;
-  let x = tint_mod(vec3<u32>(20u), vec3<u32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillIntDivMod());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// reflect for vec2<f32>
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillReflectVec2F32() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.reflect_vec2_f32 = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec2_f32) {
-    auto* src = R"(
-fn f() {
-  let e1 = vec2<f32>(1.0f);
-  let e2 = vec2<f32>(1.0f);
-  let x = reflect(e1, e2);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec2_f16) {
-    auto* src = R"(
-enable f16;
-
-fn f() {
-  let e1 = vec2<f16>(1.0h);
-  let e2 = vec2<f16>(1.0h);
-  let x = reflect(e1, e2);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let e1 = vec3<f32>(1.0f);
-  let e2 = vec3<f32>(1.0f);
-  let x = reflect(e1, e2);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec3_f16) {
-    auto* src = R"(
-enable f16;
-
-fn f() {
-  let e1 = vec3<f16>(1.0h);
-  let e2 = vec3<f16>(1.0h);
-  let x = reflect(e1, e2);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec4_f32) {
-    auto* src = R"(
-fn f() {
-  let e1 = vec3<f32>(1.0f);
-  let e2 = vec3<f32>(1.0f);
-  let x = reflect(e1, e2);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunReflect_vec4_f16) {
-    auto* src = R"(
-enable f16;
-
-fn f() {
-  let e1 = vec3<f16>(1.0h);
-  let e2 = vec3<f16>(1.0h);
-  let x = reflect(e1, e2);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, Reflect_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : vec2<f32> = reflect(vec2<f32>(1.0), vec2<f32>(1.0));
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillReflectVec2F32()));
-}
-
-TEST_F(BuiltinPolyfillTest, Reflect_vec2_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 0.5f;
-  let r : vec2<f32> = reflect(vec2<f32>(v), vec2<f32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_reflect(e1 : vec2<f32>, e2 : vec2<f32>) -> vec2<f32> {
-  let factor = (-2.0 * dot(e1, e2));
-  return (e1 + (factor * e2));
-}
-
-fn f() {
-  let v = 0.5f;
-  let r : vec2<f32> = tint_reflect(vec2<f32>(v), vec2<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillReflectVec2F32());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Reflect_multiple_types) {
-    auto* src = R"(
-enable f16;
-
-fn f() {
-  let in_f32 = 0.5f;
-  let out_f32_vec2 : vec2<f32> = reflect(vec2<f32>(in_f32), vec2<f32>(in_f32));
-  let out_f32_vec3 : vec3<f32> = reflect(vec3<f32>(in_f32), vec3<f32>(in_f32));
-  let out_f32_vec4 : vec4<f32> = reflect(vec4<f32>(in_f32), vec4<f32>(in_f32));
-  let in_f16 = 0.5h;
-  let out_f16_vec2 : vec2<f16> = reflect(vec2<f16>(in_f16), vec2<f16>(in_f16));
-  let out_f16_vec3 : vec3<f16> = reflect(vec3<f16>(in_f16), vec3<f16>(in_f16));
-  let out_f16_vec4 : vec4<f16> = reflect(vec4<f16>(in_f16), vec4<f16>(in_f16));
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-fn tint_reflect(e1 : vec2<f32>, e2 : vec2<f32>) -> vec2<f32> {
-  let factor = (-2.0 * dot(e1, e2));
-  return (e1 + (factor * e2));
-}
-
-fn f() {
-  let in_f32 = 0.5f;
-  let out_f32_vec2 : vec2<f32> = tint_reflect(vec2<f32>(in_f32), vec2<f32>(in_f32));
-  let out_f32_vec3 : vec3<f32> = reflect(vec3<f32>(in_f32), vec3<f32>(in_f32));
-  let out_f32_vec4 : vec4<f32> = reflect(vec4<f32>(in_f32), vec4<f32>(in_f32));
-  let in_f16 = 0.5h;
-  let out_f16_vec2 : vec2<f16> = reflect(vec2<f16>(in_f16), vec2<f16>(in_f16));
-  let out_f16_vec3 : vec3<f16> = reflect(vec3<f16>(in_f16), vec3<f16>(in_f16));
-  let out_f16_vec4 : vec4<f16> = reflect(vec4<f16>(in_f16), vec4<f16>(in_f16));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillReflectVec2F32());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// saturate
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillSaturate() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.saturate = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunSaturate) {
-    auto* src = R"(
-fn f() {
-  let v = 0.5f;
-  _ = saturate(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillSaturate()));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : f32 = saturate(0.5);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillSaturate()));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 0.5f;
-  let r : f32 = saturate(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_saturate(v : f32) -> f32 {
-  return clamp(v, f32(0), f32(1));
-}
-
-fn f() {
-  let v = 0.5f;
-  let r : f32 = tint_saturate(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillSaturate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_f16) {
-    auto* src = R"(
-enable f16;
-
-fn f() {
-  let v = 0.5h;
-  let r : f16 = saturate(v);
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-fn tint_saturate(v : f16) -> f16 {
-  return clamp(v, f16(0), f16(1));
-}
-
-fn f() {
-  let v = 0.5h;
-  let r : f16 = tint_saturate(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillSaturate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_vec3_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 0.5f;
-  let r : vec3<f32> = saturate(vec3<f32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_saturate(v : vec3<f32>) -> vec3<f32> {
-  return clamp(v, vec3<f32>(0), vec3<f32>(1));
-}
-
-fn f() {
-  let v = 0.5f;
-  let r : vec3<f32> = tint_saturate(vec3<f32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillSaturate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Saturate_vec3_f16) {
-    auto* src = R"(
-enable f16;
-
-fn f() {
-  let v = 0.5h;
-  let r : vec3<f16> = saturate(vec3<f16>(v));
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-fn tint_saturate(v : vec3<f16>) -> vec3<f16> {
-  return clamp(v, vec3<f16>(0), vec3<f16>(1));
-}
-
-fn f() {
-  let v = 0.5h;
-  let r : vec3<f16> = tint_saturate(vec3<f16>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillSaturate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sign_int
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillSignInt() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.sign_int = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunSign_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1i;
-  _ = sign(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillSignInt()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunSign_f32) {
-    auto* src = R"(
-fn f() {
-  let v = 1f;
-  _ = sign(v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillSignInt()));
-}
-
-TEST_F(BuiltinPolyfillTest, SignInt_ConstantExpression) {
-    auto* src = R"(
-fn f() {
-  let r : i32 = sign(1i);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillSignInt()));
-}
-
-TEST_F(BuiltinPolyfillTest, SignInt_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1i;
-  let r : i32 = sign(v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_sign(v : i32) -> i32 {
-  return select(select(-1, 1, (v > 0)), 0, (v == 0));
-}
-
-fn f() {
-  let v = 1i;
-  let r : i32 = tint_sign(v);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillSignInt());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, SignInt_vec3_i32) {
-    auto* src = R"(
-fn f() {
-  let v = 1i;
-  let r : vec3<i32> = sign(vec3<i32>(v));
-}
-)";
-
-    auto* expect = R"(
-fn tint_sign(v : vec3<i32>) -> vec3<i32> {
-  return select(select(vec3(-1), vec3(1), (v > vec3(0))), vec3(0), (v == vec3(0)));
-}
-
-fn f() {
-  let v = 1i;
-  let r : vec3<i32> = tint_sign(vec3<i32>(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillSignInt());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// textureSampleBaseClampToEdge
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillTextureSampleBaseClampToEdge_2d_f32() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.texture_sample_base_clamp_to_edge_2d_f32 = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunTextureSampleBaseClampToEdge_2d_f32) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-@group(0) @binding(1) var s : sampler;
-
-fn f() {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillTextureSampleBaseClampToEdge_2d_f32()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunTextureSampleBaseClampToEdge_external) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_external;
-@group(0) @binding(1) var s : sampler;
-
-fn f() {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillTextureSampleBaseClampToEdge_2d_f32()));
-}
-
-TEST_F(BuiltinPolyfillTest, TextureSampleBaseClampToEdge_2d_f32_f32) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-@group(0) @binding(1) var s : sampler;
-
-fn f() {
-  let r = textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
-}
-)";
-
-    auto* expect = R"(
-fn tint_textureSampleBaseClampToEdge(t : texture_2d<f32>, s : sampler, coord : vec2<f32>) -> vec4<f32> {
-  let dims = vec2<f32>(textureDimensions(t, 0));
-  let half_texel = (vec2<f32>(0.5) / dims);
-  let clamped = clamp(coord, half_texel, (1 - half_texel));
-  return textureSampleLevel(t, s, clamped, 0);
-}
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn f() {
-  let r = tint_textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillTextureSampleBaseClampToEdge_2d_f32());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// workgroupUniformLoad
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillWorkgroupUniformLoad() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.workgroup_uniform_load = true;
-
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-
-    return data;
-}
-
-DataMap polyfillWorkgroupUniformLoadWithDirectVariableAccess() {
-    DataMap data;
-
-    BuiltinPolyfill::Builtins builtins;
-    builtins.workgroup_uniform_load = true;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-
-    DirectVariableAccess::Options options;
-    data.Add<DirectVariableAccess::Config>(options);
-
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunWorkgroupUniformLoad) {
-    auto* src = R"(
-var<workgroup> v : i32;
-
-fn f() {
-  _ = workgroupUniformLoad(&v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad()));
-}
-
-TEST_F(BuiltinPolyfillTest, ShouldRunWorkgroupUniformLoadAtomic) {
-    auto* src = R"(
-var<workgroup> v : atomic<i32>;
-
-fn f() {
-  _ = workgroupUniformLoad(&v);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
-    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad()));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_i32) {
-    auto* src = R"(
-var<workgroup> v : i32;
-
-fn f() {
-  let r = workgroupUniformLoad(&v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_workgroupUniformLoad(p : ptr<workgroup, i32>) -> i32 {
-  workgroupBarrier();
-  let result = *(p);
-  workgroupBarrier();
-  return result;
-}
-
-var<workgroup> v : i32;
-
-fn f() {
-  let r = tint_workgroupUniformLoad(&(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoadAtomic_i32) {
-    auto* src = R"(
-var<workgroup> v : atomic<i32>;
-
-fn f() {
-  let r = workgroupUniformLoad(&v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_workgroupUniformLoad(p : ptr<workgroup, atomic<i32>>) -> i32 {
-  workgroupBarrier();
-  let result = atomicLoad(p);
-  workgroupBarrier();
-  return result;
-}
-
-var<workgroup> v : atomic<i32>;
-
-fn f() {
-  let r = tint_workgroupUniformLoad(&(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_ComplexType) {
-    auto* src = R"(
-struct Inner {
-  b : bool,
-  v : vec4<i32>,
-  m : mat3x3<f32>,
-}
-
-struct Outer {
-  a : array<Inner, 4>,
-}
-
-var<workgroup> v : Outer;
-
-fn f() {
-  let r = workgroupUniformLoad(&v);
-}
-)";
-
-    auto* expect = R"(
-fn tint_workgroupUniformLoad(p : ptr<workgroup, Outer>) -> Outer {
-  workgroupBarrier();
-  let result = *(p);
-  workgroupBarrier();
-  return result;
-}
-
-struct Inner {
-  b : bool,
-  v : vec4<i32>,
-  m : mat3x3<f32>,
-}
-
-struct Outer {
-  a : array<Inner, 4>,
-}
-
-var<workgroup> v : Outer;
-
-fn f() {
-  let r = tint_workgroupUniformLoad(&(v));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_AvoidDuplicateEnables) {
-    auto* src = R"(
-var<workgroup> a : i32;
-var<workgroup> b : u32;
-var<workgroup> c : f32;
-
-fn f() {
-  let ra = workgroupUniformLoad(&a);
-  let rb = workgroupUniformLoad(&b);
-  let rc = workgroupUniformLoad(&c);
-}
-)";
-
-    auto* expect = R"(
-fn tint_workgroupUniformLoad(p : ptr<workgroup, i32>) -> i32 {
-  workgroupBarrier();
-  let result = *(p);
-  workgroupBarrier();
-  return result;
-}
-
-fn tint_workgroupUniformLoad_1(p : ptr<workgroup, u32>) -> u32 {
-  workgroupBarrier();
-  let result = *(p);
-  workgroupBarrier();
-  return result;
-}
-
-fn tint_workgroupUniformLoad_2(p : ptr<workgroup, f32>) -> f32 {
-  workgroupBarrier();
-  let result = *(p);
-  workgroupBarrier();
-  return result;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : u32;
-
-var<workgroup> c : f32;
-
-fn f() {
-  let ra = tint_workgroupUniformLoad(&(a));
-  let rb = tint_workgroupUniformLoad_1(&(b));
-  let rc = tint_workgroupUniformLoad_2(&(c));
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillWorkgroupUniformLoad());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, WorkgroupUniformLoad_DirectVariableAccess) {
-    auto* src = R"(
-var<workgroup> v : i32;
-var<workgroup> v2 : i32;
-
-fn f() {
-  let r = workgroupUniformLoad(&v);
-  let s = workgroupUniformLoad(&v2);
-}
-)";
-
-    auto* expect = R"(
-fn tint_workgroupUniformLoad_v() -> i32 {
-  workgroupBarrier();
-  let result = v;
-  workgroupBarrier();
-  return result;
-}
-
-fn tint_workgroupUniformLoad_v2() -> i32 {
-  workgroupBarrier();
-  let result = v2;
-  workgroupBarrier();
-  return result;
-}
-
-var<workgroup> v : i32;
-
-var<workgroup> v2 : i32;
-
-fn f() {
-  let r = tint_workgroupUniformLoad_v();
-  let s = tint_workgroupUniformLoad_v2();
-}
-)";
-
-    auto got = Run<BuiltinPolyfill, DirectVariableAccess>(
-        src, polyfillWorkgroupUniformLoadWithDirectVariableAccess());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Built-in functions in packed_4x8_integer_dot_product
-////////////////////////////////////////////////////////////////////////////////
-DataMap polyfillPacked4x8IntegerDotProduct() {
-    BuiltinPolyfill::Builtins builtins;
-    builtins.dot_4x8_packed = true;
-    builtins.pack_unpack_4x8 = true;
-    builtins.pack_4xu8_clamp = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-    return data;
-}
-
-TEST_F(BuiltinPolyfillTest, Dot4I8Packed) {
-    auto* src = R"(
-fn f() {
-  let v1 = 0x01020304u;
-  let v2 = 0xF1F2F3F4u;
-  _ = dot4I8Packed(v1, v2);
-}
-)";
-
-    auto* expect = R"(
-fn tint_dot4_i8_packed(a : u32, b : u32) -> i32 {
-  const n = vec4<u32>(24, 16, 8, 0);
-  let a_i8 = (bitcast<vec4<i32>>((vec4<u32>(a) << n)) >> vec4<u32>(24));
-  let b_i8 = (bitcast<vec4<i32>>((vec4<u32>(b) << n)) >> vec4<u32>(24));
-  return dot(a_i8, b_i8);
-}
-
-fn f() {
-  let v1 = 16909060u;
-  let v2 = 4059231220u;
-  _ = tint_dot4_i8_packed(v1, v2);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Dot4U8Packed) {
-    auto* src = R"(
-fn f() {
-  let v1 = 0x01020304u;
-  let v2 = 0xF1F2F3F4u;
-  _ = dot4U8Packed(v1, v2);
-}
-)";
-
-    auto* expect = R"(
-fn tint_dot4_u8_packed(a : u32, b : u32) -> u32 {
-  const n = vec4<u32>(24, 16, 8, 0);
-  let a_u8 = ((vec4<u32>(a) >> n) & vec4<u32>(255));
-  let b_u8 = ((vec4<u32>(b) >> n) & vec4<u32>(255));
-  return dot(a_u8, b_u8);
-}
-
-fn f() {
-  let v1 = 16909060u;
-  let v2 = 4059231220u;
-  _ = tint_dot4_u8_packed(v1, v2);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Pack4xI8) {
-    auto* src = R"(
-fn f() {
-  let v1 = vec4i(127, 128, -128, -129);
-  _ = pack4xI8(v1);
-}
-)";
-
-    auto* expect = R"(
-fn tint_pack_4xi8(a : vec4<i32>) -> u32 {
-  const n = vec4<u32>(0, 8, 16, 24);
-  let a_u32 = bitcast<vec4<u32>>(a);
-  let a_u8 = ((a_u32 & vec4<u32>(255)) << n);
-  return dot(a_u8, vec4<u32>(1));
-}
-
-fn f() {
-  let v1 = vec4i(127, 128, -(128), -(129));
-  _ = tint_pack_4xi8(v1);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Pack4xU8) {
-    auto* src = R"(
-fn f() {
-  let v1 = vec4u(0, 254, 255, 256);
-  _ = pack4xU8(v1);
-}
-)";
-
-    auto* expect = R"(
-fn tint_pack_4xu8(a : vec4<u32>) -> u32 {
-  const n = vec4<u32>(0, 8, 16, 24);
-  let a_u8 = ((a & vec4<u32>(255)) << n);
-  return dot(a_u8, vec4<u32>(1));
-}
-
-fn f() {
-  let v1 = vec4u(0, 254, 255, 256);
-  _ = tint_pack_4xu8(v1);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Pack4xI8Clamp) {
-    auto* src = R"(
-fn f() {
-  let v1 = vec4i(127, 128, -128, -129);
-  _ = pack4xI8Clamp(v1);
-}
-)";
-
-    auto* expect = R"(
-fn tint_pack_4xi8_clamp(a : vec4<i32>) -> u32 {
-  const n = vec4<u32>(0, 8, 16, 24);
-  let a_clamp = clamp(a, vec4<i32>(-128), vec4<i32>(127));
-  let a_u32 = bitcast<vec4<u32>>(a_clamp);
-  let a_u8 = ((a_u32 & vec4<u32>(255)) << n);
-  return dot(a_u8, vec4<u32>(1));
-}
-
-fn f() {
-  let v1 = vec4i(127, 128, -(128), -(129));
-  _ = tint_pack_4xi8_clamp(v1);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Pack4xU8Clamp) {
-    auto* src = R"(
-fn f() {
-  let v1 = vec4u(0, 254, 255, 256);
-  _ = pack4xU8Clamp(v1);
-}
-)";
-
-    auto* expect = R"(
-fn tint_pack_4xu8_clamp(a : vec4<u32>) -> u32 {
-  const n = vec4<u32>(0, 8, 16, 24);
-  let a_clamp = clamp(a, vec4<u32>(0), vec4<u32>(255));
-  let a_u8 = vec4<u32>((a_clamp << n));
-  return dot(a_u8, vec4<u32>(1));
-}
-
-fn f() {
-  let v1 = vec4u(0, 254, 255, 256);
-  _ = tint_pack_4xu8_clamp(v1);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Unpack4xI8) {
-    auto* src = R"(
-fn f() {
-  let v1 = u32(0x01FF02FE);
-  _ = unpack4xI8(v1);
-}
-)";
-
-    auto* expect = R"(
-fn tint_unpack_4xi8(a : u32) -> vec4<i32> {
-  const n = vec4<u32>(24, 16, 8, 0);
-  let a_vec4u = vec4<u32>(a);
-  let a_vec4i = bitcast<vec4<i32>>((a_vec4u << n));
-  return (a_vec4i >> vec4<u32>(24));
-}
-
-fn f() {
-  let v1 = u32(33489662);
-  _ = tint_unpack_4xi8(v1);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(BuiltinPolyfillTest, Unpack4xU8) {
-    auto* src = R"(
-fn f() {
-  let v1 = u32(0xFF01FE02);
-  _ = unpack4xU8(v1);
-}
-)";
-
-    auto* expect = R"(
-fn tint_unpack_4xu8(a : u32) -> vec4<u32> {
-  const n = vec4<u32>(0, 8, 16, 24);
-  let a_vec4u = (vec4<u32>(a) >> n);
-  return (a_vec4u & vec4<u32>(255));
-}
-
-fn f() {
-  let v1 = u32(4278320642);
-  _ = tint_unpack_4xu8(v1);
-}
-)";
-
-    auto got = Run<BuiltinPolyfill>(src, polyfillPacked4x8IntegerDotProduct());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Polyfill combinations
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_F(BuiltinPolyfillTest, BitshiftAndModulo) {
-    auto* src = R"(
-fn f(x : i32, y : u32, z : u32) {
-    let l = x << (y % z);
-}
-)";
-
-    auto* expect = R"(
-fn tint_mod(lhs : u32, rhs : u32) -> u32 {
-  return (lhs % select(rhs, 1, (rhs == 0)));
-}
-
-fn f(x : i32, y : u32, z : u32) {
-  let l = (x << (tint_mod(y, z) & 31));
-}
-)";
-
-    BuiltinPolyfill::Builtins builtins;
-    builtins.bitshift_modulo = true;
-    builtins.int_div_mod = true;
-    DataMap data;
-    data.Add<BuiltinPolyfill::Config>(builtins);
-
-    auto got = Run<BuiltinPolyfill>(src, std::move(data));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
deleted file mode 100644
index 7696ef2..0000000
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
+++ /dev/null
@@ -1,826 +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/wgsl/ast/transform/canonicalize_entry_point_io.h"
-
-#include <algorithm>
-#include <string>
-#include <unordered_set>
-#include <utility>
-
-#include "src/tint/lang/core/builtin_value.h"
-#include "src/tint/lang/core/fluent_types.h"
-#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/function.h"
-#include "src/tint/utils/containers/transform.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::CanonicalizeEntryPointIO::HLSLClipDistance1);
-
-namespace tint::ast::transform {
-
-CanonicalizeEntryPointIO::CanonicalizeEntryPointIO() = default;
-CanonicalizeEntryPointIO::~CanonicalizeEntryPointIO() = default;
-
-namespace {
-
-/// Info for a struct member
-struct MemberInfo {
-    /// The struct member item
-    const StructMember* member;
-    /// The struct member location if provided
-    std::optional<uint32_t> location;
-    /// The struct member blend_src if provided
-    std::optional<uint32_t> blend_src;
-    /// The struct member color if provided
-    std::optional<uint32_t> color;
-};
-
-/// FXC is sensitive to field order in structures, this is used by StructMemberComparator to ensure
-/// that FXC is happy with the order of emitted fields.
-uint32_t BuiltinOrder(core::BuiltinValue builtin) {
-    switch (builtin) {
-        case core::BuiltinValue::kPosition:
-            return 1;
-        case core::BuiltinValue::kVertexIndex:
-            return 2;
-        case core::BuiltinValue::kInstanceIndex:
-            return 3;
-        case core::BuiltinValue::kFrontFacing:
-            return 4;
-        case core::BuiltinValue::kFragDepth:
-            return 5;
-        case core::BuiltinValue::kLocalInvocationId:
-            return 6;
-        case core::BuiltinValue::kLocalInvocationIndex:
-            return 7;
-        case core::BuiltinValue::kGlobalInvocationId:
-            return 8;
-        case core::BuiltinValue::kWorkgroupId:
-            return 9;
-        case core::BuiltinValue::kNumWorkgroups:
-            return 10;
-        case core::BuiltinValue::kSampleIndex:
-            return 11;
-        case core::BuiltinValue::kSampleMask:
-            return 12;
-        case core::BuiltinValue::kPointSize:
-            return 13;
-        case core::BuiltinValue::kClipDistances:
-            return 14;
-        default:
-            break;
-    }
-    TINT_UNREACHABLE();
-}
-
-// Returns true if `attr` is a shader IO attribute.
-bool IsShaderIOAttribute(const Attribute* attr) {
-    return attr->IsAnyOf<BuiltinAttribute, InterpolateAttribute, InvariantAttribute,
-                         LocationAttribute, ColorAttribute, BlendSrcAttribute>();
-}
-
-}  // namespace
-
-/// PIMPL state for the transform
-struct CanonicalizeEntryPointIO::State {
-    /// OutputValue represents a shader result that the wrapper function produces.
-    struct OutputValue {
-        /// The name of the output value.
-        std::string name;
-        /// The type of the output value.
-        Type type;
-        /// The shader IO attributes.
-        tint::Vector<const Attribute*, 8> attributes;
-        /// The value itself.
-        const Expression* value;
-        /// The output location.
-        std::optional<uint32_t> location;
-        /// The output blend_src.
-        std::optional<uint32_t> blend_src;
-    };
-
-    /// The clone context.
-    program::CloneContext& ctx;
-    /// The program builder
-    ProgramBuilder& b;
-    /// The entry point function (AST).
-    const Function* func_ast;
-    /// The entry point function (SEM).
-    const sem::Function* func_sem;
-
-    /// The new entry point wrapper function's parameters.
-    tint::Vector<const Parameter*, 8> wrapper_ep_parameters;
-
-    /// The members of the wrapper function's struct parameter.
-    tint::Vector<MemberInfo, 8> wrapper_struct_param_members;
-    /// The name of the wrapper function's struct parameter.
-    Symbol wrapper_struct_param_name;
-    /// The parameters that will be passed to the original function.
-    tint::Vector<const Expression*, 8> inner_call_parameters;
-    /// The members of the wrapper function's struct return type.
-    tint::Vector<MemberInfo, 8> wrapper_struct_output_members;
-    /// The wrapper function output values.
-    tint::Vector<OutputValue, 8> wrapper_output_values;
-    /// The body of the wrapper function.
-    tint::Vector<const Statement*, 8> wrapper_body;
-    /// Input names used by the entrypoint
-    std::unordered_set<std::string> input_names;
-    /// A map of cloned attribute to builtin value
-    Hashmap<const BuiltinAttribute*, core::BuiltinValue, 16> builtin_attrs;
-    /// A map of builtin values to HLSL wave intrinsic functions.
-    Hashmap<core::BuiltinValue, Symbol, 2> wave_intrinsics;
-    /// The array length of the struct member with builtin attribute `clip_distances`. Now it is
-    /// only set and used on HLSL and MSL backends.
-    uint32_t clip_distances_size = 0;
-
-    /// Constructor
-    /// @param context the clone context
-    /// @param builder the program builder
-    /// @param function the entry point function
-    State(program::CloneContext& context, ProgramBuilder& builder, const Function* function)
-        : ctx(context), b(builder), func_ast(function), func_sem(ctx.src->Sem().Get(function)) {}
-
-    /// Clones the attributes from @p in and adds it to @p out. If @p in is a builtin attribute,
-    /// then builtin_attrs is updated with the builtin information.
-    /// @param in the attribute to clone
-    /// @param out the output Attributes
-    template <size_t N>
-    void CloneAttribute(const Attribute* in, tint::Vector<const Attribute*, N>& out) {
-        auto* cloned = ctx.Clone(in);
-        out.Push(cloned);
-        if (auto* builtin = in->As<BuiltinAttribute>()) {
-            builtin_attrs.Add(cloned->As<BuiltinAttribute>(), builtin->builtin);
-        }
-    }
-
-    /// Clones the shader IO and internal attributes from @p in.
-    /// @param in the attributes to clone
-    /// @param do_interpolate whether to clone InterpolateAttribute
-    /// @return the cloned attributes
-    auto CloneShaderIOAttributes(tint::VectorRef<const Attribute*> in, bool do_interpolate) {
-        tint::Vector<const Attribute*, 8> out;
-        for (auto* attr : in) {
-            if ((IsShaderIOAttribute(attr) &&
-                 (do_interpolate || !attr->template Is<InterpolateAttribute>())) ||
-                attr->Is<ast::InternalAttribute>()) {
-                CloneAttribute(attr, out);
-            }
-        }
-        return out;
-    }
-
-    /// @param attr the input attribute
-    /// @returns the builtin value of the attribute
-    core::BuiltinValue BuiltinOf(const BuiltinAttribute* attr) {
-        if (attr->generation_id == b.ID()) {
-            // attr belongs to the target program.
-            // Obtain the builtin value from #builtin_attrs.
-            if (auto builtin = builtin_attrs.Get(attr)) {
-                return *builtin;
-            }
-        } else {
-            // attr belongs to the source program.
-            // Obtain the builtin value from the semantic info.
-            return attr->builtin;
-        }
-        TINT_ICE() << "could not obtain builtin value from attribute";
-    }
-
-    /// @param attrs the input attribute list
-    /// @returns the builtin value if any of the attributes in @p attrs is a builtin attribute,
-    /// otherwise core::BuiltinValue::kUndefined
-    core::BuiltinValue BuiltinOf(VectorRef<const Attribute*> attrs) {
-        if (auto* builtin = GetAttribute<BuiltinAttribute>(attrs)) {
-            return BuiltinOf(builtin);
-        }
-        return core::BuiltinValue::kUndefined;
-    }
-
-    /// Create or return a symbol for the wrapper function's struct parameter.
-    /// @returns the symbol for the struct parameter
-    Symbol InputStructSymbol() {
-        if (!wrapper_struct_param_name.IsValid()) {
-            wrapper_struct_param_name = b.Sym();
-        }
-        return wrapper_struct_param_name;
-    }
-
-    /// Call a wave intrinsic function for the subgroup builtin contained in @p attrs, if any.
-    /// @param attrs the attribute list that may contain a subgroup builtin
-    /// @returns an expression that calls a HLSL wave intrinsic, or nullptr
-    const ast::CallExpression* CallWaveIntrinsic(VectorRef<const Attribute*> attrs) {
-        // Helper to make a wave intrinsic.
-        auto make_intrinsic = [&](const char* name, HLSLWaveIntrinsic::Op op) {
-            auto symbol = b.Symbols().New(name);
-            b.Func(symbol, Empty, b.ty.u32(), nullptr,
-                   Vector{b.ASTNodes().Create<HLSLWaveIntrinsic>(b.ID(), b.AllocateNodeID(), op),
-                          b.Disable(DisabledValidation::kFunctionHasNoBody)});
-            return symbol;
-        };
-
-        // Get or create the intrinsic function.
-        auto builtin = BuiltinOf(attrs);
-        auto intrinsic = wave_intrinsics.GetOrAdd(builtin, [&] {
-            if (builtin == core::BuiltinValue::kSubgroupInvocationId) {
-                return make_intrinsic("__WaveGetLaneIndex",
-                                      HLSLWaveIntrinsic::Op::kWaveGetLaneIndex);
-            }
-            if (builtin == core::BuiltinValue::kSubgroupSize) {
-                return make_intrinsic("__WaveGetLaneCount",
-                                      HLSLWaveIntrinsic::Op::kWaveGetLaneCount);
-            }
-            return Symbol();
-        });
-        if (!intrinsic) {
-            return nullptr;
-        }
-
-        // Call the intrinsic function.
-        return b.Call(intrinsic);
-    }
-
-    /// Add a shader input to the entry point.
-    /// @param name the name of the shader input
-    /// @param type the type of the shader input
-    /// @param location the location if provided
-    /// @param color the color if provided
-    /// @param attrs the attributes to apply to the shader input
-    /// @returns an expression which evaluates to the value of the shader input
-    const Expression* AddInput(std::string name,
-                               const core::type::Type* type,
-                               std::optional<uint32_t> location,
-                               std::optional<uint32_t> color,
-                               tint::Vector<const Attribute*, 8> attrs) {
-        auto ast_type = CreateASTTypeFor(ctx, type);
-
-        auto builtin_attr = BuiltinOf(attrs);
-
-        // Move it to the new structure member list.
-        Symbol symbol =
-            input_names.emplace(name).second ? b.Symbols().Register(name) : b.Symbols().New(name);
-        wrapper_struct_param_members.Push({b.Member(symbol, ast_type, std::move(attrs)), location,
-                                           /* blend_src */ std::nullopt, color});
-        const Expression* expr = b.MemberAccessor(InputStructSymbol(), symbol);
-
-        // If this is a fragment position builtin, we need to invert the 'w' component of the
-        // vector.
-        if (builtin_attr == core::BuiltinValue::kPosition) {
-            auto* xyz = b.MemberAccessor(expr, "xyz");
-            auto* w = b.MemberAccessor(b.MemberAccessor(InputStructSymbol(), symbol), "w");
-            expr = b.Call<vec4<f32>>(xyz, b.Div(1_a, w));
-        }
-
-        return expr;
-    }
-
-    /// Add a shader output to the entry point.
-    /// @param name the name of the shader output
-    /// @param type the type of the shader output
-    /// @param location the location if provided
-    /// @param blend_src the blend_src if provided
-    /// @param attrs the attributes to apply to the shader output
-    /// @param value the value of the shader output
-    void AddOutput(std::string name,
-                   const core::type::Type* type,
-                   std::optional<uint32_t> location,
-                   std::optional<uint32_t> blend_src,
-                   tint::Vector<const Attribute*, 8> attrs,
-                   const Expression* value) {
-        auto builtin_attr = BuiltinOf(attrs);
-
-        if (func_ast->PipelineStage() == PipelineStage::kVertex &&
-            builtin_attr == core::BuiltinValue::kClipDistances) {
-            const auto* arrayType = 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 {
-                clip_distances_size = *arrayType->ConstantCount();
-            }
-        }
-
-        auto ast_type = CreateASTTypeFor(ctx, type);
-
-        OutputValue output;
-        output.name = name;
-        output.type = ast_type;
-        output.attributes = std::move(attrs);
-        output.value = value;
-        output.location = location;
-        output.blend_src = blend_src;
-        wrapper_output_values.Push(output);
-    }
-
-    /// Process a non-struct parameter.
-    /// This creates a new object for the shader input, moving the shader IO
-    /// attributes to it. It also adds an expression to the list of parameters
-    /// that will be passed to the original function.
-    /// @param param the original function parameter
-    void ProcessNonStructParameter(const sem::Parameter* param) {
-        if (auto* wave_intrinsic_call = CallWaveIntrinsic(param->Declaration()->attributes)) {
-            inner_call_parameters.Push(wave_intrinsic_call);
-            for (auto* attr : param->Declaration()->attributes) {
-                ctx.Remove(param->Declaration()->attributes, attr);
-            }
-            return;
-        }
-
-        // Do not add interpolation attributes on vertex input
-        bool do_interpolate = func_ast->PipelineStage() != PipelineStage::kVertex;
-        // Remove the shader IO attributes from the inner function parameter, and attach them to the
-        // new object instead.
-        tint::Vector<const Attribute*, 8> attributes;
-        for (auto* attr : param->Declaration()->attributes) {
-            if (IsShaderIOAttribute(attr)) {
-                ctx.Remove(param->Declaration()->attributes, attr);
-                if ((do_interpolate || !attr->Is<InterpolateAttribute>())) {
-                    CloneAttribute(attr, attributes);
-                }
-            }
-        }
-
-        auto name = param->Declaration()->name->symbol.Name();
-        auto* input_expr = AddInput(name, param->Type(), param->Attributes().location,
-                                    param->Attributes().color, std::move(attributes));
-        inner_call_parameters.Push(input_expr);
-    }
-
-    /// Process a struct parameter.
-    /// This creates new objects for each struct member, moving the shader IO
-    /// attributes to them. It also creates the structure that will be passed to
-    /// the original function.
-    /// @param param the original function parameter
-    void ProcessStructParameter(const sem::Parameter* param) {
-        // Do not add interpolation attributes on vertex input
-        bool do_interpolate = func_ast->PipelineStage() != PipelineStage::kVertex;
-
-        auto* str = param->Type()->As<sem::Struct>();
-
-        // Recreate struct members in the outer entry point and build an initializer
-        // list to pass them through to the inner function.
-        tint::Vector<const Expression*, 8> inner_struct_values;
-        for (auto* member : str->Members()) {
-            if (DAWN_UNLIKELY(member->Type()->Is<core::type::Struct>())) {
-                TINT_ICE() << "nested IO struct";
-            }
-
-            if (auto* wave_intrinsic_call = CallWaveIntrinsic(member->Declaration()->attributes)) {
-                inner_struct_values.Push(wave_intrinsic_call);
-                continue;
-            }
-
-            auto name = member->Name().Name();
-
-            auto attributes =
-                CloneShaderIOAttributes(member->Declaration()->attributes, do_interpolate);
-            auto* input_expr = AddInput(name, member->Type(), member->Attributes().location,
-                                        member->Attributes().color, std::move(attributes));
-            inner_struct_values.Push(input_expr);
-        }
-
-        // Construct the original structure using the new shader input objects.
-        inner_call_parameters.Push(
-            b.Call(ctx.Clone(param->Declaration()->type), inner_struct_values));
-    }
-
-    /// Process the entry point return type.
-    /// This generates a list of output values that are returned by the original
-    /// function.
-    /// @param inner_ret_type the original function return type
-    /// @param original_result the result object produced by the original function
-    void ProcessReturnType(const core::type::Type* inner_ret_type, Symbol original_result) {
-        // Do not add interpolation attributes on fragment output
-        bool do_interpolate = func_ast->PipelineStage() != PipelineStage::kFragment;
-        if (auto* str = inner_ret_type->As<sem::Struct>()) {
-            for (auto* member : str->Members()) {
-                if (DAWN_UNLIKELY(member->Type()->Is<core::type::Struct>())) {
-                    TINT_ICE() << "nested IO struct";
-                }
-
-                auto name = member->Name().Name();
-                auto attributes =
-                    CloneShaderIOAttributes(member->Declaration()->attributes, do_interpolate);
-
-                // Extract the original structure member.
-                AddOutput(name, member->Type(), member->Attributes().location,
-                          member->Attributes().blend_src, std::move(attributes),
-                          b.MemberAccessor(original_result, name));
-            }
-        } else if (!inner_ret_type->Is<core::type::Void>()) {
-            auto attributes =
-                CloneShaderIOAttributes(func_ast->return_type_attributes, do_interpolate);
-
-            // Propagate the non-struct return value as is.
-            AddOutput("value", func_sem->ReturnType(), func_sem->ReturnLocation(),
-                      func_sem->ReturnIndex(), std::move(attributes), b.Expr(original_result));
-        }
-    }
-
-    /// Comparison function used to reorder struct members such that all members with
-    /// color attributes appear first (ordered by color slot), then location attributes (ordered by
-    /// location slot), then blend_src attributes (ordered by blend_src slot), followed by those
-    /// with builtin attributes (ordered by BuiltinOrder).
-    /// @param x a struct member
-    /// @param y another struct member
-    /// @returns true if a comes before b
-    bool StructMemberComparator(const MemberInfo& x, const MemberInfo& y) {
-        if (x.color.has_value() && y.color.has_value() && x.color != y.color) {
-            // Both have color attributes: smallest goes first.
-            return x.color < y.color;
-        } else if (x.color.has_value() != y.color.has_value()) {
-            // The member with the color goes first
-            return x.color.has_value();
-        }
-
-        if (x.location.has_value() && y.location.has_value() && x.location != y.location) {
-            // Both have location attributes: smallest goes first.
-            return x.location < y.location;
-        } else if (x.location.has_value() != y.location.has_value()) {
-            // The member with the location goes first
-            return x.location.has_value();
-        }
-
-        if (x.blend_src.has_value() && y.blend_src.has_value() && x.blend_src != y.blend_src) {
-            // Both have blend_src attributes: smallest goes first.
-            return x.blend_src < y.blend_src;
-        } else if (x.blend_src.has_value() != y.blend_src.has_value()) {
-            // The member with the blend_src goes first
-            return x.blend_src.has_value();
-        }
-
-        {
-            auto* x_blt = GetAttribute<BuiltinAttribute>(x.member->attributes);
-            auto* y_blt = GetAttribute<BuiltinAttribute>(y.member->attributes);
-            if (x_blt && y_blt) {
-                // Both are builtins: order matters for FXC.
-                auto builtin_a = BuiltinOf(x_blt);
-                auto builtin_b = BuiltinOf(y_blt);
-                auto order_a = BuiltinOrder(builtin_a);
-                auto order_b = BuiltinOrder(builtin_b);
-                if (order_a != order_b) {
-                    return order_a < order_b;
-                }
-            } else if ((x_blt != nullptr) != (y_blt != nullptr)) {
-                // The member with the builtin goes first
-                return x_blt != nullptr;
-            }
-        }
-
-        // Control flow reaches here if x is the same as y.
-        // Sort algorithms sometimes do that.
-        return false;
-    }
-
-    /// Create the wrapper function's struct parameter and type objects.
-    void CreateInputStruct() {
-        // Sort the struct members to satisfy HLSL interfacing matching rules.
-        std::sort(wrapper_struct_param_members.begin(), wrapper_struct_param_members.end(),
-                  [&](auto& x, auto& y) { return StructMemberComparator(x, y); });
-
-        auto members =
-            tint::Transform(wrapper_struct_param_members, [](auto& info) { return info.member; });
-
-        // Create the new struct type.
-        auto struct_name = b.Sym();
-        auto* in_struct = b.create<Struct>(b.Ident(struct_name), std::move(members), Empty);
-        ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), func_ast, in_struct);
-
-        // Create a new function parameter using this struct type.
-        auto* param = b.Param(InputStructSymbol(), b.ty(struct_name));
-        wrapper_ep_parameters.Push(param);
-    }
-
-    /// Get an existing member symbol from a set of member names or create a new symbol
-    /// @param member_names the set of the existing member names
-    /// @param symbol_name the name of the symbol
-    /// @returns the symbol that represents the member name we want
-    Symbol GetOrCreateMemberName(std::unordered_set<std::string>* member_names,
-                                 std::string symbol_name) {
-        Symbol member_name;
-        if (member_names->count(symbol_name)) {
-            member_name = b.Symbols().New(symbol_name);
-        } else {
-            member_name = b.Symbols().Register(symbol_name);
-        }
-        member_names->insert(member_name.Name());
-        return member_name;
-    }
-
-    /// Handle `clip_distances` in HLSL
-    /// @param assignments the assignments to the members of the output wrapper struct
-    /// @param member_names the set that contains all the member names in the output wrapper struct
-    /// @param clip_distances_inner_array_name the `clip_distances` member in inner struct
-    /// @param wrapper_struct_name the symbol of the output wrapper struct
-    /// @param old_struct_member_attributes the attributes of the old `clip_distances` member
-    void HandleClipDistancesInHLSL(
-        tint::Vector<const Statement*, 8>* assignments,
-        std::unordered_set<std::string>* member_names,
-        const Symbol& clip_distances_inner_array_name,
-        const Symbol& wrapper_struct_name,
-        tint::Vector<const Attribute*, 8>&& old_struct_member_attributes) {
-        auto TranslateClipDistancesIntoF32 =
-            [&](const Symbol& new_member, core::u32 inner_array_index,
-                tint::VectorRef<const ast::Attribute*>&& new_member_attributes) {
-                assignments->Push(
-                    b.Assign(b.MemberAccessor(wrapper_struct_name, new_member),
-                             b.IndexAccessor(clip_distances_inner_array_name, inner_array_index)));
-                wrapper_struct_output_members.Push({
-                    /* member */ b.Member(new_member, b.ty.f32(), std::move(new_member_attributes)),
-                    /* location */ std::nullopt,
-                    /* blend_src */ std::nullopt,
-                    /* color */ std::nullopt,
-                });
-            };
-
-        auto TranslateClipDistancesIntoVector =
-            [&](const Symbol& new_member, uint32_t vector_size, uint32_t inner_array_offset,
-                tint::VectorRef<const ast::Attribute*>&& new_member_attributes) {
-                for (uint32_t i = 0; i < vector_size; ++i) {
-                    assignments->Push(b.Assign(
-                        b.IndexAccessor(b.MemberAccessor(wrapper_struct_name, new_member), u32(i)),
-                        b.IndexAccessor(clip_distances_inner_array_name,
-                                        u32(inner_array_offset + i))));
-                }
-                wrapper_struct_output_members.Push({
-                    /* member */ b.Member(new_member, b.ty.vec(b.ty.f32(), vector_size),
-                                          std::move(new_member_attributes)),
-                    /* location */ std::nullopt,
-                    /* blend_src */ std::nullopt,
-                    /* color */ std::nullopt,
-                });
-            };
-
-        // float clip_distance_0 : SV_ClipDistance0;
-        auto attribute_vector_0 = std::move(old_struct_member_attributes);
-        attribute_vector_0.Push(b.Disable(ast::DisabledValidation::kIgnoreClipDistancesType));
-        auto new_member_0 = GetOrCreateMemberName(member_names, "clip_distance_0");
-        if (clip_distances_size == 1u) {
-            TranslateClipDistancesIntoF32(new_member_0, 0_u, std::move(attribute_vector_0));
-            return;
-        }
-
-        // floatN clip_distance_0 : SV_ClipDistance0;
-        uint32_t clip_distance0_size = std::min(clip_distances_size, 4u);
-        TranslateClipDistancesIntoVector(new_member_0, clip_distance0_size, 0,
-                                         std::move(attribute_vector_0));
-
-        // It is enough to just generate SV_ClipDistance0.
-        if (clip_distances_size <= 4u) {
-            return;
-        }
-
-        // float clip_distance_1 : SV_ClipDistance1;
-        auto attribute_vector_1 =
-            Vector{b.ASTNodes().Create<HLSLClipDistance1>(b.ID(), b.AllocateNodeID())};
-        auto new_member_1 = GetOrCreateMemberName(member_names, "clip_distance_1");
-
-        if (clip_distances_size == 5u) {
-            TranslateClipDistancesIntoF32(new_member_1, 4_u, std::move(attribute_vector_1));
-            return;
-        }
-
-        // floatN clip_distance_1 : SV_ClipDistance1;
-        uint32_t clip_distances1_size = clip_distances_size - 4u;
-        TranslateClipDistancesIntoVector(new_member_1, clip_distances1_size, 4u,
-                                         std::move(attribute_vector_1));
-    }
-
-    /// Create and return the wrapper function's struct result object.
-    /// @returns the struct type
-    Struct* CreateOutputStruct() {
-        tint::Vector<const Statement*, 8> assignments;
-
-        auto wrapper_result = b.Symbols().New("wrapper_result");
-
-        // Create the struct members and their corresponding assignment statements.
-        std::unordered_set<std::string> member_names;
-        for (auto& outval : wrapper_output_values) {
-            auto* builtin_attribute = GetAttribute<BuiltinAttribute>(outval.attributes);
-            if (builtin_attribute != nullptr &&
-                builtin_attribute->builtin == core::BuiltinValue::kClipDistances) {
-                Symbol clip_distances_inner_array = b.Symbols().New("tmp_inner_clip_distances");
-
-                // Consume outval.type in the let statement as in HLSL `SV_ClipDistance` always has
-                // different type.
-                assignments.Push(
-                    b.Decl(b.Let(clip_distances_inner_array, outval.type, outval.value)));
-                HandleClipDistancesInHLSL(&assignments, &member_names, clip_distances_inner_array,
-                                          wrapper_result, std::move(outval.attributes));
-                continue;
-            }
-
-            // Use the original output name, unless that is already taken.
-            Symbol name = GetOrCreateMemberName(&member_names, outval.name);
-
-            assignments.Push(b.Assign(b.MemberAccessor(wrapper_result, name), outval.value));
-
-            wrapper_struct_output_members.Push({
-                /* member */ b.Member(name, outval.type, std::move(outval.attributes)),
-                /* location */ outval.location,
-                /* blend_src */ outval.blend_src,
-                /* color */ std::nullopt,
-            });
-        }
-
-        // Sort the struct members to satisfy HLSL interfacing matching rules.
-        std::sort(wrapper_struct_output_members.begin(), wrapper_struct_output_members.end(),
-                  [&](auto& x, auto& y) { return StructMemberComparator(x, y); });
-
-        tint::Vector<const StructMember*, 8> members;
-        for (auto& mem : wrapper_struct_output_members) {
-            members.Push(mem.member);
-        }
-
-        // Create the new struct type.
-        auto* out_struct = b.create<Struct>(b.Ident(b.Sym()), std::move(members), Empty);
-        ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), func_ast, out_struct);
-
-        // Create the output struct object, assign its members, and return it.
-        auto* result_object = b.Var(wrapper_result, b.ty(out_struct->name->symbol));
-        wrapper_body.Push(b.Decl(result_object));
-        for (auto* assignment : assignments) {
-            wrapper_body.Push(assignment);
-        }
-        wrapper_body.Push(b.Return(wrapper_result));
-
-        return out_struct;
-    }
-
-    // Recreate the original function without entry point attributes and call it.
-    /// @returns the inner function call expression
-    const CallExpression* CallInnerFunction() {
-        // Add a suffix to the function name, as the wrapper function will take the original entry
-        // point name.
-        auto ep_name = func_ast->name->symbol.Name();
-        auto inner_name = b.Symbols().New(ep_name + "_inner");
-
-        // Clone everything, dropping the function and return type attributes.
-        // The parameter attributes will have already been stripped during
-        // processing.
-        auto* inner_function = b.create<Function>(b.Ident(inner_name), ctx.Clone(func_ast->params),
-                                                  ctx.Clone(func_ast->return_type),
-                                                  ctx.Clone(func_ast->body), Empty, Empty);
-        ctx.Replace(func_ast, inner_function);
-
-        // Call the function.
-        return b.Call(inner_function->name->symbol, inner_call_parameters);
-    }
-
-    /// Process the entry point function.
-    void Process() {
-        // Exit early if there is no shader IO to handle.
-        if (func_sem->Parameters().Length() == 0 &&
-            func_sem->ReturnType()->Is<core::type::Void>()) {
-            return;
-        }
-
-        // Process the entry point parameters, collecting those that need to be
-        // aggregated into a single structure.
-        if (!func_sem->Parameters().IsEmpty()) {
-            for (auto* param : func_sem->Parameters()) {
-                if (param->Type()->Is<core::type::Struct>()) {
-                    ProcessStructParameter(param);
-                } else {
-                    ProcessNonStructParameter(param);
-                }
-            }
-
-            // Create a structure parameter for the outer entry point if necessary.
-            if (!wrapper_struct_param_members.IsEmpty()) {
-                CreateInputStruct();
-            }
-        }
-
-        // Recreate the original function and call it.
-        auto* call_inner = CallInnerFunction();
-
-        // Process the return type, and start building the wrapper function body.
-        std::function<Type()> wrapper_ret_type = [&] { return b.ty.void_(); };
-        if (func_sem->ReturnType()->Is<core::type::Void>()) {
-            // The function call is just a statement with no result.
-            wrapper_body.Push(b.CallStmt(call_inner));
-        } else {
-            // Capture the result of calling the original function.
-            auto* inner_result = b.Let(b.Symbols().New("inner_result"), call_inner);
-            wrapper_body.Push(b.Decl(inner_result));
-
-            // Process the original return type to determine the outputs that the
-            // outer function needs to produce.
-            ProcessReturnType(func_sem->ReturnType(), inner_result->name->symbol);
-        }
-
-        // Produce the entry point outputs, if necessary.
-        if (!wrapper_output_values.IsEmpty()) {
-            auto* output_struct = CreateOutputStruct();
-            wrapper_ret_type = [&, output_struct] { return b.ty(output_struct->name->symbol); };
-        }
-
-        // Create the wrapper entry point function.
-        Symbol name = ctx.Clone(func_ast->name->symbol);
-        auto* wrapper_func =
-            b.create<Function>(b.Ident(name), wrapper_ep_parameters, b.ty(wrapper_ret_type()),
-                               b.Block(wrapper_body), ctx.Clone(func_ast->attributes), Empty);
-        ctx.InsertAfter(ctx.src->AST().GlobalDeclarations(), func_ast, wrapper_func);
-    }
-};
-
-Transform::ApplyResult CanonicalizeEntryPointIO::Apply(const Program& src,
-                                                       const DataMap&,
-                                                       DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    // Remove entry point IO attributes from struct declarations.
-    // New structures will be created for each entry point, as necessary.
-    for (auto* ty : src.AST().TypeDecls()) {
-        if (auto* struct_ty = ty->As<Struct>()) {
-            for (auto* member : struct_ty->members) {
-                for (auto* attr : member->attributes) {
-                    if (IsShaderIOAttribute(attr)) {
-                        ctx.Remove(member->attributes, attr);
-                    }
-                }
-            }
-        }
-    }
-
-    for (auto* func_ast : src.AST().Functions()) {
-        if (!func_ast->IsEntryPoint()) {
-            continue;
-        }
-
-        State state(ctx, b, func_ast);
-        state.Process();
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-CanonicalizeEntryPointIO::HLSLWaveIntrinsic::HLSLWaveIntrinsic(GenerationID pid, NodeID nid, Op o)
-    : Base(pid, nid, Empty), op(o) {}
-CanonicalizeEntryPointIO::HLSLWaveIntrinsic::~HLSLWaveIntrinsic() = default;
-std::string CanonicalizeEntryPointIO::HLSLWaveIntrinsic::InternalName() const {
-    StringStream ss;
-    switch (op) {
-        case Op::kWaveGetLaneCount:
-            return "intrinsic_wave_get_lane_count";
-        case Op::kWaveGetLaneIndex:
-            return "intrinsic_wave_get_lane_index";
-    }
-    return ss.str();
-}
-
-const CanonicalizeEntryPointIO::HLSLWaveIntrinsic*
-CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Clone(ast::CloneContext& ctx) const {
-    return ctx.dst->ASTNodes().Create<CanonicalizeEntryPointIO::HLSLWaveIntrinsic>(
-        ctx.dst->ID(), ctx.dst->AllocateNodeID(), op);
-}
-
-CanonicalizeEntryPointIO::HLSLClipDistance1::HLSLClipDistance1(GenerationID pid, NodeID nid)
-    : Base(pid, nid, Empty) {}
-
-CanonicalizeEntryPointIO::HLSLClipDistance1::~HLSLClipDistance1() = default;
-
-std::string CanonicalizeEntryPointIO::HLSLClipDistance1::InternalName() const {
-    return "SV_ClipDistance1";
-}
-
-const CanonicalizeEntryPointIO::HLSLClipDistance1*
-CanonicalizeEntryPointIO::HLSLClipDistance1::Clone(ast::CloneContext& ctx) const {
-    return ctx.dst->ASTNodes().Create<CanonicalizeEntryPointIO::HLSLClipDistance1>(
-        ctx.dst->ID(), ctx.dst->AllocateNodeID());
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
deleted file mode 100644
index 39eb99b..0000000
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h
+++ /dev/null
@@ -1,168 +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_WGSL_AST_TRANSFORM_CANONICALIZE_ENTRY_POINT_IO_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_CANONICALIZE_ENTRY_POINT_IO_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/reflection.h"
-
-namespace tint::ast::transform {
-
-/// CanonicalizeEntryPointIO is a transform used to rewrite shader entry point
-/// interfaces into a form that the generators can handle. Each entry point
-/// function is stripped of all shader IO attributes and wrapped in a function
-/// that provides the shader interface.
-/// The transform config determines whether to use global variables, structures,
-/// or parameters for the shader inputs and outputs, and optionally adds
-/// additional builtins to the shader interface.
-///
-/// Before:
-/// ```
-/// struct Locations{
-///   @location(1) loc1 : f32;
-///   @location(2) loc2 : vec4<u32>;
-/// };
-///
-/// @fragment
-/// fn frag_main(@builtin(position) coord : vec4<f32>,
-///              locations : Locations) -> @location(0) f32 {
-///   if (coord.w > 1.0) {
-///     return 0.0;
-///   }
-///   var col : f32 = (coord.x * locations.loc1);
-///   return col;
-/// }
-/// ```
-///
-/// After (using structures for all parameters):
-/// ```
-/// struct Locations{
-///   loc1 : f32;
-///   loc2 : vec4<u32>;
-/// };
-///
-/// struct frag_main_in {
-///   @builtin(position) coord : vec4<f32>;
-///   @location(1) loc1 : f32;
-///   @location(2) loc2 : vec4<u32>
-/// };
-///
-/// struct frag_main_out {
-///   @location(0) loc0 : f32;
-/// };
-///
-/// fn frag_main_inner(coord : vec4<f32>,
-///                    locations : Locations) -> f32 {
-///   if (coord.w > 1.0) {
-///     return 0.0;
-///   }
-///   var col : f32 = (coord.x * locations.loc1);
-///   return col;
-/// }
-///
-/// @fragment
-/// fn frag_main(in : frag_main_in) -> frag_main_out {
-///   let inner_retval = frag_main_inner(in.coord, Locations(in.loc1, in.loc2));
-///   var wrapper_result : frag_main_out;
-///   wrapper_result.loc0 = inner_retval;
-///   return wrapper_result;
-/// }
-/// ```
-///
-/// @note Depends on the following transforms to have been run first:
-/// * Unshadow
-class CanonicalizeEntryPointIO final : public Castable<CanonicalizeEntryPointIO, Transform> {
-  public:
-    /// HLSLWaveIntrinsic is an InternalAttribute that is used to decorate a stub function so that
-    /// the HLSL backend transforms this into calls to Wave* intrinsic functions.
-    class HLSLWaveIntrinsic final : public Castable<HLSLWaveIntrinsic, InternalAttribute> {
-      public:
-        /// Wave intrinsic op
-        enum class Op {
-            kWaveGetLaneIndex,
-            kWaveGetLaneCount,
-        };
-
-        /// Constructor
-        /// @param pid the identifier of the program that owns this node
-        /// @param nid the unique node identifier
-        /// @param o the op of the wave intrinsic
-        HLSLWaveIntrinsic(GenerationID pid, NodeID nid, Op o);
-        /// Destructor
-        ~HLSLWaveIntrinsic() override;
-
-        /// @copydoc InternalAttribute::InternalName
-        std::string InternalName() const override;
-
-        /// Performs a deep clone of this object using the program::CloneContext `ctx`.
-        /// @param ctx the clone context
-        /// @return the newly cloned object
-        const HLSLWaveIntrinsic* Clone(CloneContext& ctx) const override;
-
-        /// The op of the intrinsic
-        const Op op;
-    };
-
-    /// HLSLClipDistance1 is an InternalAttribute that is used to represent `SV_ClipDistance1`.
-    class HLSLClipDistance1 final : public Castable<HLSLClipDistance1, InternalAttribute> {
-      public:
-        /// Constructor
-        /// @param pid the identifier of the program that owns this node
-        /// @param nid the unique node identifier
-        HLSLClipDistance1(GenerationID pid, NodeID nid);
-        /// Destructor
-        ~HLSLClipDistance1() override;
-
-        /// @copydoc InternalAttribute::InternalName
-        std::string InternalName() const override;
-
-        /// Performs a deep clone of this object using the program::CloneContext `ctx`.
-        /// @param ctx the clone context
-        /// @return the newly cloned object
-        const HLSLClipDistance1* Clone(CloneContext& ctx) const override;
-    };
-
-    /// Constructor
-    CanonicalizeEntryPointIO();
-    ~CanonicalizeEntryPointIO() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_CANONICALIZE_ENTRY_POINT_IO_H_
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
deleted file mode 100644
index 8808eaf..0000000
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io_test.cc
+++ /dev/null
@@ -1,1716 +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/wgsl/ast/transform/canonicalize_entry_point_io.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using CanonicalizeEntryPointIOTest = TransformTest;
-
-TEST_F(CanonicalizeEntryPointIOTest, NoShaderIO) {
-    // Test that we do not introduce wrapper functions when there is no shader IO
-    // to process.
-    auto* src = R"(
-@fragment
-fn frag_main() {
-}
-
-@compute @workgroup_size(1)
-fn comp_main() {
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Parameters_Hlsl) {
-    auto* src = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-@fragment
-fn frag_main(@location(1) loc1 : f32,
-             @location(2) @interpolate(flat) loc2 : vec4<u32>,
-             @builtin(position) coord : vec4<f32>,
-             @color(3) color : vec4<f32>) {
-  var col : f32 = (coord.x * loc1) + color.g;
-}
-)";
-
-    auto* expect = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct tint_symbol_1 {
-  @color(3)
-  color : vec4<f32>,
-  @location(1)
-  loc1 : f32,
-  @location(2) @interpolate(flat)
-  loc2 : vec4<u32>,
-  @builtin(position)
-  coord : vec4<f32>,
-}
-
-fn frag_main_inner(loc1 : f32, loc2 : vec4<u32>, coord : vec4<f32>, color : vec4<f32>) {
-  var col : f32 = ((coord.x * loc1) + color.g);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
-  frag_main_inner(tint_symbol.loc1, tint_symbol.loc2, vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w)), tint_symbol.color);
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Parameter_TypeAlias) {
-    auto* src = R"(
-alias myf32 = f32;
-
-@fragment
-fn frag_main(@location(1) loc1 : myf32) {
-  var x : myf32 = loc1;
-}
-)";
-
-    auto* expect = R"(
-alias myf32 = f32;
-
-struct tint_symbol_1 {
-  @location(1)
-  loc1 : f32,
-}
-
-fn frag_main_inner(loc1 : myf32) {
-  var x : myf32 = loc1;
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
-  frag_main_inner(tint_symbol.loc1);
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Parameter_TypeAlias_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn frag_main(@location(1) loc1 : myf32) {
-  var x : myf32 = loc1;
-}
-
-alias myf32 = f32;
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  @location(1)
-  loc1 : f32,
-}
-
-fn frag_main_inner(loc1 : myf32) {
-  var x : myf32 = loc1;
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
-  frag_main_inner(tint_symbol.loc1);
-}
-
-alias myf32 = f32;
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Hlsl) {
-    auto* src = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct FragBuiltins {
-  @builtin(position) coord : vec4<f32>,
-};
-struct FragLocations {
-  @location(1) loc1 : f32,
-  @location(2) @interpolate(flat) loc2 : vec4<u32>,
-};
-struct FragColors {
-  @color(3) col3 : vec4<f32>,
-  @color(1) col1 : vec4<u32>,
-  @color(2) col2 : vec4<i32>,
-};
-
-@fragment
-fn frag_main(@location(0) loc0 : f32,
-             locations : FragLocations,
-             builtins : FragBuiltins,
-             colors : FragColors) {
-  var col : f32 = (((builtins.coord.x * locations.loc1) + loc0) + colors.col3.g);
-}
-)";
-
-    auto* expect = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct FragBuiltins {
-  coord : vec4<f32>,
-}
-
-struct FragLocations {
-  loc1 : f32,
-  loc2 : vec4<u32>,
-}
-
-struct FragColors {
-  col3 : vec4<f32>,
-  col1 : vec4<u32>,
-  col2 : vec4<i32>,
-}
-
-struct tint_symbol_1 {
-  @color(1)
-  col1 : vec4<u32>,
-  @color(2)
-  col2 : vec4<i32>,
-  @color(3)
-  col3 : vec4<f32>,
-  @location(0)
-  loc0 : f32,
-  @location(1)
-  loc1 : f32,
-  @location(2) @interpolate(flat)
-  loc2 : vec4<u32>,
-  @builtin(position)
-  coord : vec4<f32>,
-}
-
-fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins, colors : FragColors) {
-  var col : f32 = (((builtins.coord.x * locations.loc1) + loc0) + colors.col3.g);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
-  frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w))), FragColors(tint_symbol.col3, tint_symbol.col1, tint_symbol.col2));
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, StructParameters_Hlsl_OutOfOrder) {
-    auto* src = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-@fragment
-fn frag_main(@location(0) loc0 : f32,
-             locations : FragLocations,
-             builtins : FragBuiltins,
-             colors : FragColors) {
-  var col : f32 = (((builtins.coord.x * locations.loc1) + loc0) + colors.col3.g);
-}
-
-struct FragBuiltins {
-  @builtin(position) coord : vec4<f32>,
-};
-struct FragLocations {
-  @location(1) loc1 : f32,
-  @location(2) @interpolate(flat) loc2 : vec4<u32>,
-};
-struct FragColors {
-  @color(3) col3 : vec4<f32>,
-  @color(1) col1 : vec4<u32>,
-  @color(2) col2 : vec4<i32>,
-};
-)";
-
-    auto* expect = R"(
-enable chromium_experimental_framebuffer_fetch;
-
-struct tint_symbol_1 {
-  @color(1)
-  col1 : vec4<u32>,
-  @color(2)
-  col2 : vec4<i32>,
-  @color(3)
-  col3 : vec4<f32>,
-  @location(0)
-  loc0 : f32,
-  @location(1)
-  loc1 : f32,
-  @location(2) @interpolate(flat)
-  loc2 : vec4<u32>,
-  @builtin(position)
-  coord : vec4<f32>,
-}
-
-fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins, colors : FragColors) {
-  var col : f32 = (((builtins.coord.x * locations.loc1) + loc0) + colors.col3.g);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
-  frag_main_inner(tint_symbol.loc0, FragLocations(tint_symbol.loc1, tint_symbol.loc2), FragBuiltins(vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w))), FragColors(tint_symbol.col3, tint_symbol.col1, tint_symbol.col2));
-}
-
-struct FragBuiltins {
-  coord : vec4<f32>,
-}
-
-struct FragLocations {
-  loc1 : f32,
-  loc2 : vec4<u32>,
-}
-
-struct FragColors {
-  col3 : vec4<f32>,
-  col1 : vec4<u32>,
-  col2 : vec4<i32>,
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Return_NonStruct_Hlsl) {
-    auto* src = R"(
-@fragment
-fn frag_main() -> @builtin(frag_depth) f32 {
-  return 1.0;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @builtin(frag_depth)
-  value : f32,
-}
-
-fn frag_main_inner() -> f32 {
-  return 1.0;
-}
-
-@fragment
-fn frag_main() -> tint_symbol {
-  let inner_result = frag_main_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.value = inner_result;
-  return wrapper_result;
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Hlsl) {
-    auto* src = R"(
-struct FragOutput {
-  @location(0) color : vec4<f32>,
-  @builtin(frag_depth) depth : f32,
-  @builtin(sample_mask) mask : u32,
-};
-
-@fragment
-fn frag_main() -> FragOutput {
-  var output : FragOutput;
-  output.depth = 1.0;
-  output.mask = 7u;
-  output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
-  return output;
-}
-)";
-
-    auto* expect = R"(
-struct FragOutput {
-  color : vec4<f32>,
-  depth : f32,
-  mask : u32,
-}
-
-struct tint_symbol {
-  @location(0)
-  color : vec4<f32>,
-  @builtin(frag_depth)
-  depth : f32,
-  @builtin(sample_mask)
-  mask : u32,
-}
-
-fn frag_main_inner() -> FragOutput {
-  var output : FragOutput;
-  output.depth = 1.0;
-  output.mask = 7u;
-  output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
-  return output;
-}
-
-@fragment
-fn frag_main() -> tint_symbol {
-  let inner_result = frag_main_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.color = inner_result.color;
-  wrapper_result.depth = inner_result.depth;
-  wrapper_result.mask = inner_result.mask;
-  return wrapper_result;
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Hlsl_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn frag_main() -> FragOutput {
-  var output : FragOutput;
-  output.depth = 1.0;
-  output.mask = 7u;
-  output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
-  return output;
-}
-
-struct FragOutput {
-  @location(0) color : vec4<f32>,
-  @builtin(frag_depth) depth : f32,
-  @builtin(sample_mask) mask : u32,
-};
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @location(0)
-  color : vec4<f32>,
-  @builtin(frag_depth)
-  depth : f32,
-  @builtin(sample_mask)
-  mask : u32,
-}
-
-fn frag_main_inner() -> FragOutput {
-  var output : FragOutput;
-  output.depth = 1.0;
-  output.mask = 7u;
-  output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
-  return output;
-}
-
-@fragment
-fn frag_main() -> tint_symbol {
-  let inner_result = frag_main_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.color = inner_result.color;
-  wrapper_result.depth = inner_result.depth;
-  wrapper_result.mask = inner_result.mask;
-  return wrapper_result;
-}
-
-struct FragOutput {
-  color : vec4<f32>,
-  depth : f32,
-  mask : u32,
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction_Hlsl) {
-    auto* src = R"(
-struct FragmentInput {
-  @location(0) value : f32,
-  @location(1) mul : f32,
-};
-
-fn foo(x : FragmentInput) -> f32 {
-  return x.value * x.mul;
-}
-
-@fragment
-fn frag_main1(inputs : FragmentInput) {
-  var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main2(inputs : FragmentInput) {
-  var x : f32 = foo(inputs);
-}
-)";
-
-    auto* expect = R"(
-struct FragmentInput {
-  value : f32,
-  mul : f32,
-}
-
-fn foo(x : FragmentInput) -> f32 {
-  return (x.value * x.mul);
-}
-
-struct tint_symbol_1 {
-  @location(0)
-  value : f32,
-  @location(1)
-  mul : f32,
-}
-
-fn frag_main1_inner(inputs : FragmentInput) {
-  var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main1(tint_symbol : tint_symbol_1) {
-  frag_main1_inner(FragmentInput(tint_symbol.value, tint_symbol.mul));
-}
-
-struct tint_symbol_3 {
-  @location(0)
-  value : f32,
-  @location(1)
-  mul : f32,
-}
-
-fn frag_main2_inner(inputs : FragmentInput) {
-  var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main2(tint_symbol_2 : tint_symbol_3) {
-  frag_main2_inner(FragmentInput(tint_symbol_2.value, tint_symbol_2.mul));
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, StructParameters_SharedDeviceFunction_Hlsl_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn frag_main1(inputs : FragmentInput) {
-  var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main2(inputs : FragmentInput) {
-  var x : f32 = foo(inputs);
-}
-
-fn foo(x : FragmentInput) -> f32 {
-  return x.value * x.mul;
-}
-
-struct FragmentInput {
-  @location(0) value : f32,
-  @location(1) mul : f32,
-};
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  @location(0)
-  value : f32,
-  @location(1)
-  mul : f32,
-}
-
-fn frag_main1_inner(inputs : FragmentInput) {
-  var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main1(tint_symbol : tint_symbol_1) {
-  frag_main1_inner(FragmentInput(tint_symbol.value, tint_symbol.mul));
-}
-
-struct tint_symbol_3 {
-  @location(0)
-  value : f32,
-  @location(1)
-  mul : f32,
-}
-
-fn frag_main2_inner(inputs : FragmentInput) {
-  var x : f32 = foo(inputs);
-}
-
-@fragment
-fn frag_main2(tint_symbol_2 : tint_symbol_3) {
-  frag_main2_inner(FragmentInput(tint_symbol_2.value, tint_symbol_2.mul));
-}
-
-fn foo(x : FragmentInput) -> f32 {
-  return (x.value * x.mul);
-}
-
-struct FragmentInput {
-  value : f32,
-  mul : f32,
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable) {
-    auto* src = R"(
-struct FragmentInput {
-  @location(0) col1 : f32,
-  @location(1) col2 : f32,
-};
-
-var<private> global_inputs : FragmentInput;
-
-fn foo() -> f32 {
-  return global_inputs.col1 * 0.5;
-}
-
-fn bar() -> f32 {
-  return global_inputs.col2 * 2.0;
-}
-
-@fragment
-fn frag_main1(inputs : FragmentInput) {
- global_inputs = inputs;
- var r : f32 = foo();
- var g : f32 = bar();
-}
-)";
-
-    auto* expect = R"(
-struct FragmentInput {
-  col1 : f32,
-  col2 : f32,
-}
-
-var<private> global_inputs : FragmentInput;
-
-fn foo() -> f32 {
-  return (global_inputs.col1 * 0.5);
-}
-
-fn bar() -> f32 {
-  return (global_inputs.col2 * 2.0);
-}
-
-struct tint_symbol_1 {
-  @location(0)
-  col1 : f32,
-  @location(1)
-  col2 : f32,
-}
-
-fn frag_main1_inner(inputs : FragmentInput) {
-  global_inputs = inputs;
-  var r : f32 = foo();
-  var g : f32 = bar();
-}
-
-@fragment
-fn frag_main1(tint_symbol : tint_symbol_1) {
-  frag_main1_inner(FragmentInput(tint_symbol.col1, tint_symbol.col2));
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_ModuleScopeVariable_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn frag_main1(inputs : FragmentInput) {
- global_inputs = inputs;
- var r : f32 = foo();
- var g : f32 = bar();
-}
-
-fn foo() -> f32 {
-  return global_inputs.col1 * 0.5;
-}
-
-fn bar() -> f32 {
-  return global_inputs.col2 * 2.0;
-}
-
-var<private> global_inputs : FragmentInput;
-
-struct FragmentInput {
-  @location(0) col1 : f32,
-  @location(1) col2 : f32,
-};
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  @location(0)
-  col1 : f32,
-  @location(1)
-  col2 : f32,
-}
-
-fn frag_main1_inner(inputs : FragmentInput) {
-  global_inputs = inputs;
-  var r : f32 = foo();
-  var g : f32 = bar();
-}
-
-@fragment
-fn frag_main1(tint_symbol : tint_symbol_1) {
-  frag_main1_inner(FragmentInput(tint_symbol.col1, tint_symbol.col2));
-}
-
-fn foo() -> f32 {
-  return (global_inputs.col1 * 0.5);
-}
-
-fn bar() -> f32 {
-  return (global_inputs.col2 * 2.0);
-}
-
-var<private> global_inputs : FragmentInput;
-
-struct FragmentInput {
-  col1 : f32,
-  col2 : f32,
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases) {
-    auto* src = R"(
-alias myf32 = f32;
-
-struct FragmentInput {
-  @location(0) col1 : myf32,
-  @location(1) col2 : myf32,
-};
-
-struct FragmentOutput {
-  @location(0) col1 : myf32,
-  @location(1) col2 : myf32,
-};
-
-alias MyFragmentInput = FragmentInput;
-
-alias MyFragmentOutput = FragmentOutput;
-
-fn foo(x : MyFragmentInput) -> myf32 {
-  return x.col1;
-}
-
-@fragment
-fn frag_main(inputs : MyFragmentInput) -> MyFragmentOutput {
-  var x : myf32 = foo(inputs);
-  return MyFragmentOutput(x, inputs.col2);
-}
-)";
-
-    auto* expect = R"(
-alias myf32 = f32;
-
-struct FragmentInput {
-  col1 : myf32,
-  col2 : myf32,
-}
-
-struct FragmentOutput {
-  col1 : myf32,
-  col2 : myf32,
-}
-
-alias MyFragmentInput = FragmentInput;
-
-alias MyFragmentOutput = FragmentOutput;
-
-fn foo(x : MyFragmentInput) -> myf32 {
-  return x.col1;
-}
-
-struct tint_symbol_1 {
-  @location(0)
-  col1 : f32,
-  @location(1)
-  col2 : f32,
-}
-
-struct tint_symbol_2 {
-  @location(0)
-  col1 : f32,
-  @location(1)
-  col2 : f32,
-}
-
-fn frag_main_inner(inputs : MyFragmentInput) -> MyFragmentOutput {
-  var x : myf32 = foo(inputs);
-  return MyFragmentOutput(x, inputs.col2);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
-  let inner_result = frag_main_inner(MyFragmentInput(tint_symbol.col1, tint_symbol.col2));
-  var wrapper_result : tint_symbol_2;
-  wrapper_result.col1 = inner_result.col1;
-  wrapper_result.col2 = inner_result.col2;
-  return wrapper_result;
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_TypeAliases_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn frag_main(inputs : MyFragmentInput) -> MyFragmentOutput {
-  var x : myf32 = foo(inputs);
-  return MyFragmentOutput(x, inputs.col2);
-}
-
-alias MyFragmentInput = FragmentInput;
-
-alias MyFragmentOutput = FragmentOutput;
-
-fn foo(x : MyFragmentInput) -> myf32 {
-  return x.col1;
-}
-
-struct FragmentInput {
-  @location(0) col1 : myf32,
-  @location(1) col2 : myf32,
-};
-
-struct FragmentOutput {
-  @location(0) col1 : myf32,
-  @location(1) col2 : myf32,
-};
-
-alias myf32 = f32;
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  @location(0)
-  col1 : f32,
-  @location(1)
-  col2 : f32,
-}
-
-struct tint_symbol_2 {
-  @location(0)
-  col1 : f32,
-  @location(1)
-  col2 : f32,
-}
-
-fn frag_main_inner(inputs : MyFragmentInput) -> MyFragmentOutput {
-  var x : myf32 = foo(inputs);
-  return MyFragmentOutput(x, inputs.col2);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
-  let inner_result = frag_main_inner(MyFragmentInput(tint_symbol.col1, tint_symbol.col2));
-  var wrapper_result : tint_symbol_2;
-  wrapper_result.col1 = inner_result.col1;
-  wrapper_result.col2 = inner_result.col2;
-  return wrapper_result;
-}
-
-alias MyFragmentInput = FragmentInput;
-
-alias MyFragmentOutput = FragmentOutput;
-
-fn foo(x : MyFragmentInput) -> myf32 {
-  return x.col1;
-}
-
-struct FragmentInput {
-  col1 : myf32,
-  col2 : myf32,
-}
-
-struct FragmentOutput {
-  col1 : myf32,
-  col2 : myf32,
-}
-
-alias myf32 = f32;
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes) {
-    auto* src = R"(
-struct VertexOut {
-  @builtin(position) pos : vec4<f32>,
-  @location(1) @interpolate(flat) loc1 : f32,
-  @location(2) @interpolate(linear, sample) loc2 : f32,
-  @location(3) @interpolate(perspective, centroid) loc3 : f32,
-};
-
-struct FragmentIn {
-  @location(1) @interpolate(flat) loc1 : f32,
-  @location(2) @interpolate(linear, sample) loc2 : f32,
-};
-
-@vertex
-fn vert_main() -> VertexOut {
-  return VertexOut();
-}
-
-@fragment
-fn frag_main(inputs : FragmentIn,
-             @location(3) @interpolate(perspective, centroid) loc3 : f32) {
-  let x = inputs.loc1 + inputs.loc2 + loc3;
-}
-)";
-
-    auto* expect = R"(
-struct VertexOut {
-  pos : vec4<f32>,
-  loc1 : f32,
-  loc2 : f32,
-  loc3 : f32,
-}
-
-struct FragmentIn {
-  loc1 : f32,
-  loc2 : f32,
-}
-
-struct tint_symbol {
-  @location(1) @interpolate(flat)
-  loc1 : f32,
-  @location(2) @interpolate(linear, sample)
-  loc2 : f32,
-  @location(3) @interpolate(perspective, centroid)
-  loc3 : f32,
-  @builtin(position)
-  pos : vec4<f32>,
-}
-
-fn vert_main_inner() -> VertexOut {
-  return VertexOut();
-}
-
-@vertex
-fn vert_main() -> tint_symbol {
-  let inner_result = vert_main_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.pos = inner_result.pos;
-  wrapper_result.loc1 = inner_result.loc1;
-  wrapper_result.loc2 = inner_result.loc2;
-  wrapper_result.loc3 = inner_result.loc3;
-  return wrapper_result;
-}
-
-struct tint_symbol_2 {
-  @location(1) @interpolate(flat)
-  loc1 : f32,
-  @location(2) @interpolate(linear, sample)
-  loc2 : f32,
-  @location(3) @interpolate(perspective, centroid)
-  loc3 : f32,
-}
-
-fn frag_main_inner(inputs : FragmentIn, loc3 : f32) {
-  let x = ((inputs.loc1 + inputs.loc2) + loc3);
-}
-
-@fragment
-fn frag_main(tint_symbol_1 : tint_symbol_2) {
-  frag_main_inner(FragmentIn(tint_symbol_1.loc1, tint_symbol_1.loc2), tint_symbol_1.loc3);
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, InterpolateAttributes_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn frag_main(inputs : FragmentIn,
-             @location(3) @interpolate(perspective, centroid) loc3 : f32) {
-  let x = inputs.loc1 + inputs.loc2 + loc3;
-}
-
-@vertex
-fn vert_main() -> VertexOut {
-  return VertexOut();
-}
-
-struct VertexOut {
-  @builtin(position) pos : vec4<f32>,
-  @location(1) @interpolate(flat) loc1 : f32,
-  @location(2) @interpolate(linear, sample) loc2 : f32,
-  @location(3) @interpolate(perspective, centroid) loc3 : f32,
-};
-
-struct FragmentIn {
-  @location(1) @interpolate(flat) loc1: f32,
-  @location(2) @interpolate(linear, sample) loc2 : f32,
-};
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  @location(1) @interpolate(flat)
-  loc1 : f32,
-  @location(2) @interpolate(linear, sample)
-  loc2 : f32,
-  @location(3) @interpolate(perspective, centroid)
-  loc3 : f32,
-}
-
-fn frag_main_inner(inputs : FragmentIn, loc3 : f32) {
-  let x = ((inputs.loc1 + inputs.loc2) + loc3);
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) {
-  frag_main_inner(FragmentIn(tint_symbol.loc1, tint_symbol.loc2), tint_symbol.loc3);
-}
-
-struct tint_symbol_2 {
-  @location(1) @interpolate(flat)
-  loc1 : f32,
-  @location(2) @interpolate(linear, sample)
-  loc2 : f32,
-  @location(3) @interpolate(perspective, centroid)
-  loc3 : f32,
-  @builtin(position)
-  pos : vec4<f32>,
-}
-
-fn vert_main_inner() -> VertexOut {
-  return VertexOut();
-}
-
-@vertex
-fn vert_main() -> tint_symbol_2 {
-  let inner_result = vert_main_inner();
-  var wrapper_result : tint_symbol_2;
-  wrapper_result.pos = inner_result.pos;
-  wrapper_result.loc1 = inner_result.loc1;
-  wrapper_result.loc2 = inner_result.loc2;
-  wrapper_result.loc3 = inner_result.loc3;
-  return wrapper_result;
-}
-
-struct VertexOut {
-  pos : vec4<f32>,
-  loc1 : f32,
-  loc2 : f32,
-  loc3 : f32,
-}
-
-struct FragmentIn {
-  loc1 : f32,
-  loc2 : f32,
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes) {
-    auto* src = R"(
-struct VertexOut {
-  @builtin(position) @invariant pos : vec4<f32>,
-};
-
-@vertex
-fn main1() -> VertexOut {
-  return VertexOut();
-}
-
-@vertex
-fn main2() -> @builtin(position) @invariant vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-struct VertexOut {
-  pos : vec4<f32>,
-}
-
-struct tint_symbol {
-  @builtin(position) @invariant
-  pos : vec4<f32>,
-}
-
-fn main1_inner() -> VertexOut {
-  return VertexOut();
-}
-
-@vertex
-fn main1() -> tint_symbol {
-  let inner_result = main1_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.pos = inner_result.pos;
-  return wrapper_result;
-}
-
-struct tint_symbol_1 {
-  @builtin(position) @invariant
-  value : vec4<f32>,
-}
-
-fn main2_inner() -> vec4<f32> {
-  return vec4<f32>();
-}
-
-@vertex
-fn main2() -> tint_symbol_1 {
-  let inner_result_1 = main2_inner();
-  var wrapper_result_1 : tint_symbol_1;
-  wrapper_result_1.value = inner_result_1;
-  return wrapper_result_1;
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, InvariantAttributes_OutOfOrder) {
-    auto* src = R"(
-@vertex
-fn main1() -> VertexOut {
-  return VertexOut();
-}
-
-@vertex
-fn main2() -> @builtin(position) @invariant vec4<f32> {
-  return vec4<f32>();
-}
-
-struct VertexOut {
-  @builtin(position) @invariant pos : vec4<f32>,
-};
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @builtin(position) @invariant
-  pos : vec4<f32>,
-}
-
-fn main1_inner() -> VertexOut {
-  return VertexOut();
-}
-
-@vertex
-fn main1() -> tint_symbol {
-  let inner_result = main1_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.pos = inner_result.pos;
-  return wrapper_result;
-}
-
-struct tint_symbol_1 {
-  @builtin(position) @invariant
-  value : vec4<f32>,
-}
-
-fn main2_inner() -> vec4<f32> {
-  return vec4<f32>();
-}
-
-@vertex
-fn main2() -> tint_symbol_1 {
-  let inner_result_1 = main2_inner();
-  var wrapper_result_1 : tint_symbol_1;
-  wrapper_result_1.value = inner_result_1;
-  return wrapper_result_1;
-}
-
-struct VertexOut {
-  pos : vec4<f32>,
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutAttributes) {
-    auto* src = R"(
-struct FragmentInput {
-  @size(16) @location(1) value : f32,
-  @builtin(position) @align(32) coord : vec4<f32>,
-  @location(0) @interpolate(linear, sample) @align(128) loc0 : f32,
-};
-
-struct FragmentOutput {
-  @size(16) @location(1) @interpolate(flat) value : f32,
-};
-
-@fragment
-fn frag_main(inputs : FragmentInput) -> FragmentOutput {
-  return FragmentOutput(inputs.coord.x * inputs.value + inputs.loc0);
-}
-)";
-
-    auto* expect = R"(
-struct FragmentInput {
-  @size(16)
-  value : f32,
-  @align(32)
-  coord : vec4<f32>,
-  @align(128)
-  loc0 : f32,
-}
-
-struct FragmentOutput {
-  @size(16)
-  value : f32,
-}
-
-struct tint_symbol_1 {
-  @location(0) @interpolate(linear, sample)
-  loc0 : f32,
-  @location(1)
-  value : f32,
-  @builtin(position)
-  coord : vec4<f32>,
-}
-
-struct tint_symbol_2 {
-  @location(1)
-  value : f32,
-}
-
-fn frag_main_inner(inputs : FragmentInput) -> FragmentOutput {
-  return FragmentOutput(((inputs.coord.x * inputs.value) + inputs.loc0));
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
-  let inner_result = frag_main_inner(FragmentInput(tint_symbol.value, vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w)), tint_symbol.loc0));
-  var wrapper_result : tint_symbol_2;
-  wrapper_result.value = inner_result.value;
-  return wrapper_result;
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Struct_LayoutAttributes_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn frag_main(inputs : FragmentInput) -> FragmentOutput {
-  return FragmentOutput(inputs.coord.x * inputs.value + inputs.loc0);
-}
-
-struct FragmentInput {
-  @size(16) @location(1) value : f32,
-  @builtin(position) @align(32) coord : vec4<f32>,
-  @location(0) @interpolate(linear, sample) @align(128) loc0 : f32,
-};
-
-struct FragmentOutput {
-  @size(16) @location(1) @interpolate(flat) value : f32,
-};
-)";
-
-    auto* expect = R"(
-struct tint_symbol_1 {
-  @location(0) @interpolate(linear, sample)
-  loc0 : f32,
-  @location(1)
-  value : f32,
-  @builtin(position)
-  coord : vec4<f32>,
-}
-
-struct tint_symbol_2 {
-  @location(1)
-  value : f32,
-}
-
-fn frag_main_inner(inputs : FragmentInput) -> FragmentOutput {
-  return FragmentOutput(((inputs.coord.x * inputs.value) + inputs.loc0));
-}
-
-@fragment
-fn frag_main(tint_symbol : tint_symbol_1) -> tint_symbol_2 {
-  let inner_result = frag_main_inner(FragmentInput(tint_symbol.value, vec4<f32>(tint_symbol.coord.xyz, (1 / tint_symbol.coord.w)), tint_symbol.loc0));
-  var wrapper_result : tint_symbol_2;
-  wrapper_result.value = inner_result.value;
-  return wrapper_result;
-}
-
-struct FragmentInput {
-  @size(16)
-  value : f32,
-  @align(32)
-  coord : vec4<f32>,
-  @align(128)
-  loc0 : f32,
-}
-
-struct FragmentOutput {
-  @size(16)
-  value : f32,
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, SortedMembers) {
-    auto* src = R"(
-struct VertexOutput {
-  @location(1) @interpolate(flat) b : u32,
-  @builtin(position) pos : vec4<f32>,
-  @location(3) @interpolate(flat) d : u32,
-  @location(0) a : f32,
-  @location(2) @interpolate(flat) c : i32,
-};
-
-struct FragmentInputExtra {
-  @location(3) @interpolate(flat) d : u32,
-  @builtin(position) pos : vec4<f32>,
-  @location(0) a : f32,
-};
-
-@vertex
-fn vert_main() -> VertexOutput {
-  return VertexOutput();
-}
-
-@fragment
-fn frag_main(@builtin(front_facing) ff : bool,
-             @location(2) @interpolate(flat) c : i32,
-             inputs : FragmentInputExtra,
-             @location(1) @interpolate(flat) b : u32) {
-}
-)";
-
-    auto* expect = R"(
-struct VertexOutput {
-  b : u32,
-  pos : vec4<f32>,
-  d : u32,
-  a : f32,
-  c : i32,
-}
-
-struct FragmentInputExtra {
-  d : u32,
-  pos : vec4<f32>,
-  a : f32,
-}
-
-struct tint_symbol {
-  @location(0)
-  a : f32,
-  @location(1) @interpolate(flat)
-  b : u32,
-  @location(2) @interpolate(flat)
-  c : i32,
-  @location(3) @interpolate(flat)
-  d : u32,
-  @builtin(position)
-  pos : vec4<f32>,
-}
-
-fn vert_main_inner() -> VertexOutput {
-  return VertexOutput();
-}
-
-@vertex
-fn vert_main() -> tint_symbol {
-  let inner_result = vert_main_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.b = inner_result.b;
-  wrapper_result.pos = inner_result.pos;
-  wrapper_result.d = inner_result.d;
-  wrapper_result.a = inner_result.a;
-  wrapper_result.c = inner_result.c;
-  return wrapper_result;
-}
-
-struct tint_symbol_2 {
-  @location(0)
-  a : f32,
-  @location(1) @interpolate(flat)
-  b : u32,
-  @location(2) @interpolate(flat)
-  c : i32,
-  @location(3) @interpolate(flat)
-  d : u32,
-  @builtin(position)
-  pos : vec4<f32>,
-  @builtin(front_facing)
-  ff : bool,
-}
-
-fn frag_main_inner(ff : bool, c : i32, inputs : FragmentInputExtra, b : u32) {
-}
-
-@fragment
-fn frag_main(tint_symbol_1 : tint_symbol_2) {
-  frag_main_inner(tint_symbol_1.ff, tint_symbol_1.c, FragmentInputExtra(tint_symbol_1.d, vec4<f32>(tint_symbol_1.pos.xyz, (1 / tint_symbol_1.pos.w)), tint_symbol_1.a), tint_symbol_1.b);
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, SortedMembers_OutOfOrder) {
-    auto* src = R"(
-@vertex
-fn vert_main() -> VertexOutput {
-  return VertexOutput();
-}
-
-@fragment
-fn frag_main(@builtin(front_facing) ff : bool,
-             @location(2) @interpolate(flat) c : i32,
-             inputs : FragmentInputExtra,
-             @location(1) @interpolate(flat) b : u32) {
-}
-
-struct VertexOutput {
-  @location(1) @interpolate(flat) b : u32,
-  @builtin(position) pos : vec4<f32>,
-  @location(3) @interpolate(flat) d : u32,
-  @location(0) a : f32,
-  @location(2) @interpolate(flat) c : i32,
-};
-
-struct FragmentInputExtra {
-  @location(3) @interpolate(flat) d : u32,
-  @builtin(position) pos : vec4<f32>,
-  @location(0) a : f32,
-};
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @location(0)
-  a : f32,
-  @location(1) @interpolate(flat)
-  b : u32,
-  @location(2) @interpolate(flat)
-  c : i32,
-  @location(3) @interpolate(flat)
-  d : u32,
-  @builtin(position)
-  pos : vec4<f32>,
-}
-
-fn vert_main_inner() -> VertexOutput {
-  return VertexOutput();
-}
-
-@vertex
-fn vert_main() -> tint_symbol {
-  let inner_result = vert_main_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.b = inner_result.b;
-  wrapper_result.pos = inner_result.pos;
-  wrapper_result.d = inner_result.d;
-  wrapper_result.a = inner_result.a;
-  wrapper_result.c = inner_result.c;
-  return wrapper_result;
-}
-
-struct tint_symbol_2 {
-  @location(0)
-  a : f32,
-  @location(1) @interpolate(flat)
-  b : u32,
-  @location(2) @interpolate(flat)
-  c : i32,
-  @location(3) @interpolate(flat)
-  d : u32,
-  @builtin(position)
-  pos : vec4<f32>,
-  @builtin(front_facing)
-  ff : bool,
-}
-
-fn frag_main_inner(ff : bool, c : i32, inputs : FragmentInputExtra, b : u32) {
-}
-
-@fragment
-fn frag_main(tint_symbol_1 : tint_symbol_2) {
-  frag_main_inner(tint_symbol_1.ff, tint_symbol_1.c, FragmentInputExtra(tint_symbol_1.d, vec4<f32>(tint_symbol_1.pos.xyz, (1 / tint_symbol_1.pos.w)), tint_symbol_1.a), tint_symbol_1.b);
-}
-
-struct VertexOutput {
-  b : u32,
-  pos : vec4<f32>,
-  d : u32,
-  a : f32,
-  c : i32,
-}
-
-struct FragmentInputExtra {
-  d : u32,
-  pos : vec4<f32>,
-  a : f32,
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, DontRenameSymbols) {
-    auto* src = R"(
-@fragment
-fn tint_symbol_1(@location(0) col : f32) {
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  @location(0)
-  col : f32,
-}
-
-fn tint_symbol_1_inner(col : f32) {
-}
-
-@fragment
-fn tint_symbol_1(tint_symbol : tint_symbol_2) {
-  tint_symbol_1_inner(tint_symbol.col);
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, Return_Struct_Index_Attribute_Hlsl) {
-    auto* src = R"(
-enable dual_source_blending;
-
-struct FragOutput {
-  @location(0) @blend_src(0) color : vec4<f32>,
-  @location(0) @blend_src(1) blend : vec4<f32>,
-  @builtin(frag_depth) depth : f32,
-  @builtin(sample_mask) mask : u32,
-};
-
-@fragment
-fn frag_main() -> FragOutput {
-  var output : FragOutput;
-  output.depth = 1.0;
-  output.mask = 7u;
-  output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
-  output.blend = vec4<f32>(0.5, 0.5, 0.5, 1.0);
-  return output;
-}
-)";
-
-    auto* expect = R"(
-enable dual_source_blending;
-
-struct FragOutput {
-  color : vec4<f32>,
-  blend : vec4<f32>,
-  depth : f32,
-  mask : u32,
-}
-
-struct tint_symbol {
-  @location(0) @blend_src(0)
-  color : vec4<f32>,
-  @location(0) @blend_src(1)
-  blend : vec4<f32>,
-  @builtin(frag_depth)
-  depth : f32,
-  @builtin(sample_mask)
-  mask : u32,
-}
-
-fn frag_main_inner() -> FragOutput {
-  var output : FragOutput;
-  output.depth = 1.0;
-  output.mask = 7u;
-  output.color = vec4<f32>(0.5, 0.5, 0.5, 1.0);
-  output.blend = vec4<f32>(0.5, 0.5, 0.5, 1.0);
-  return output;
-}
-
-@fragment
-fn frag_main() -> tint_symbol {
-  let inner_result = frag_main_inner();
-  var wrapper_result : tint_symbol;
-  wrapper_result.color = inner_result.color;
-  wrapper_result.blend = inner_result.blend;
-  wrapper_result.depth = inner_result.depth;
-  wrapper_result.mask = inner_result.mask;
-  return wrapper_result;
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, SubgroupBuiltins_Hlsl) {
-    auto* src = R"(
-enable subgroups;
-
-@compute @workgroup_size(64)
-fn frag_main(@builtin(subgroup_invocation_id) id : u32,
-             @builtin(subgroup_size) size : u32) {
-  let x = size - id;
-}
-)";
-
-    auto* expect = R"(
-enable subgroups;
-
-@internal(intrinsic_wave_get_lane_index) @internal(disable_validation__function_has_no_body)
-fn __WaveGetLaneIndex() -> u32
-
-@internal(intrinsic_wave_get_lane_count) @internal(disable_validation__function_has_no_body)
-fn __WaveGetLaneCount() -> u32
-
-fn frag_main_inner(id : u32, size : u32) {
-  let x = (size - id);
-}
-
-@compute @workgroup_size(64)
-fn frag_main() {
-  frag_main_inner(__WaveGetLaneIndex(), __WaveGetLaneCount());
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CanonicalizeEntryPointIOTest, SubgroupBuiltinsStruct_Hlsl) {
-    auto* src = R"(
-enable subgroups;
-
-struct Inputs {
-  @builtin(subgroup_invocation_id) id : u32,
-  @builtin(subgroup_size) size : u32,
-}
-
-@compute @workgroup_size(64)
-fn frag_main(inputs : Inputs) {
-  let x = inputs.size - inputs.id;
-}
-)";
-
-    auto* expect = R"(
-enable subgroups;
-
-@internal(intrinsic_wave_get_lane_index) @internal(disable_validation__function_has_no_body)
-fn __WaveGetLaneIndex() -> u32
-
-@internal(intrinsic_wave_get_lane_count) @internal(disable_validation__function_has_no_body)
-fn __WaveGetLaneCount() -> u32
-
-struct Inputs {
-  id : u32,
-  size : u32,
-}
-
-fn frag_main_inner(inputs : Inputs) {
-  let x = (inputs.size - inputs.id);
-}
-
-@compute @workgroup_size(64)
-fn frag_main() {
-  frag_main_inner(Inputs(__WaveGetLaneIndex(), __WaveGetLaneCount()));
-}
-)";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/demote_to_helper.cc b/src/tint/lang/wgsl/ast/transform/demote_to_helper.cc
deleted file mode 100644
index 5f8de69..0000000
--- a/src/tint/lang/wgsl/ast/transform/demote_to_helper.cc
+++ /dev/null
@@ -1,228 +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/wgsl/ast/transform/demote_to_helper.h"
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include "src/tint/lang/core/type/reference.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.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/block_statement.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"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::DemoteToHelper);
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::ast::transform {
-
-DemoteToHelper::DemoteToHelper() = default;
-
-DemoteToHelper::~DemoteToHelper() = default;
-
-Transform::ApplyResult DemoteToHelper::Apply(const Program& src, const DataMap&, DataMap&) const {
-    auto& sem = src.Sem();
-
-    // Collect the set of functions that need to be processed.
-    // A function needs to be processed if it is reachable by a shader that contains a discard at
-    // any point in its call hierarchy.
-    std::unordered_set<const sem::Function*> functions_to_process;
-    for (auto* func : src.AST().Functions()) {
-        if (!func->IsEntryPoint()) {
-            continue;
-        }
-
-        // Determine whether this entry point and its callees need to be transformed.
-        bool needs_transform = false;
-        if (sem.Get(func)->DiscardStatement()) {
-            needs_transform = true;
-        } else {
-            for (auto* callee : sem.Get(func)->TransitivelyCalledFunctions()) {
-                if (callee->DiscardStatement()) {
-                    needs_transform = true;
-                    break;
-                }
-            }
-        }
-        if (!needs_transform) {
-            continue;
-        }
-
-        // Process the entry point and its callees.
-        functions_to_process.insert(sem.Get(func));
-        for (auto* callee : sem.Get(func)->TransitivelyCalledFunctions()) {
-            functions_to_process.insert(callee);
-        }
-    }
-
-    if (functions_to_process.empty()) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    // Create a module-scope flag that indicates whether the current invocation has been discarded.
-    auto flag = b.Symbols().New("tint_discarded");
-    b.GlobalVar(flag, core::AddressSpace::kPrivate, b.Expr(false));
-
-    // Replace all discard statements with a statement that marks the invocation as discarded.
-    ctx.ReplaceAll(
-        [&](const DiscardStatement*) -> const Statement* { return b.Assign(flag, b.Expr(true)); });
-
-    // Insert a conditional discard at the end of each entry point that does not end with a return.
-    for (auto* func : functions_to_process) {
-        if (func->Declaration()->IsEntryPoint()) {
-            auto* sem_body = sem.Get(func->Declaration()->body);
-            if (sem_body->Behaviors().Contains(sem::Behavior::kNext)) {
-                ctx.InsertBack(func->Declaration()->body->statements,
-                               b.If(flag, b.Block(b.Discard())));
-            }
-        }
-    }
-
-    HoistToDeclBefore hoist_to_decl_before(ctx);
-
-    // Mask all writes to host-visible memory using the discarded flag.
-    // We also insert a discard statement before all return statements in entry points for shaders
-    // that discard.
-    std::unordered_map<const core::type::Type*, Symbol> atomic_cmpxchg_result_types;
-    for (auto* node : src.ASTNodes().Objects()) {
-        Switch(
-            node,
-
-            // Mask assignments to storage buffer variables.
-            [&](const AssignmentStatement* assign) {
-                // Skip writes in functions that are not called from shaders that discard.
-                auto* func = sem.Get(assign)->Function();
-                if (functions_to_process.count(func) == 0) {
-                    return;
-                }
-
-                // Skip phony assignments.
-                if (assign->lhs->Is<PhonyExpression>()) {
-                    return;
-                }
-
-                // Skip writes to invocation-private address spaces.
-                auto* ref = sem.GetVal(assign->lhs)->Type()->As<core::type::Reference>();
-                switch (ref->AddressSpace()) {
-                    case core::AddressSpace::kStorage:
-                        // Need to mask these.
-                        break;
-                    case core::AddressSpace::kFunction:
-                    case core::AddressSpace::kPrivate:
-                    case core::AddressSpace::kOut:
-                        // Skip these.
-                        return;
-                    default:
-                        TINT_UNREACHABLE()
-                            << "write to unhandled address space: " << ref->AddressSpace();
-                }
-
-                // If the RHS has side effects (which may contain derivative operations), we need to
-                // hoist it out to a separate declaration so that it does not get masked.
-                auto* rhs = sem.GetVal(assign->rhs);
-                if (rhs->HasSideEffects()) {
-                    hoist_to_decl_before.Add(rhs, assign->rhs,
-                                             HoistToDeclBefore::VariableKind::kLet);
-                }
-
-                // Mask the assignment using the invocation-discarded flag.
-                ctx.Replace(assign, b.If(b.Not(flag), b.Block(ctx.Clone(assign))));
-            },
-
-            // Mask builtins that write to host-visible memory.
-            [&](const CallExpression* call) {
-                auto* sem_call = sem.Get<sem::Call>(call);
-                auto* stmt = sem_call ? sem_call->Stmt() : nullptr;
-                auto* func = stmt ? stmt->Function() : nullptr;
-                auto* builtin = sem_call ? sem_call->Target()->As<sem::BuiltinFn>() : nullptr;
-                if (functions_to_process.count(func) == 0 || !builtin) {
-                    return;
-                }
-
-                if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
-                    // A call to textureStore() will always be a statement.
-                    // Wrap it inside a conditional block.
-                    auto* masked_call = b.If(b.Not(flag), b.Block(ctx.Clone(stmt->Declaration())));
-                    ctx.Replace(stmt->Declaration(), masked_call);
-                } else if (builtin->IsAtomic() && builtin->Fn() != wgsl::BuiltinFn::kAtomicLoad) {
-                    // A call to an atomic builtin can be a statement or an expression.
-                    if (auto* call_stmt = stmt->Declaration()->As<CallStatement>();
-                        call_stmt && call_stmt->expr == call) {
-                        // This call is a statement.
-                        // Wrap it inside a conditional block.
-                        auto* masked_call = b.If(b.Not(flag), b.Block(ctx.Clone(call_stmt)));
-                        ctx.Replace(stmt->Declaration(), masked_call);
-                    } else {
-                        // This call is an expression.
-                        // We transform:
-                        //   let y = x + atomicAdd(&p, 1);
-                        // Into:
-                        //   var tmp : i32;
-                        //   if (!tint_discarded) {
-                        //     tmp = atomicAdd(&p, 1);
-                        //   }
-                        //   let y = x + tmp;
-                        auto result = b.Sym();
-                        auto result_ty = CreateASTTypeFor(ctx, sem_call->Type());
-                        auto* masked_call =
-                            b.If(b.Not(flag),
-                                 b.Block(b.Assign(result, ctx.CloneWithoutTransform(call))));
-                        auto* result_decl = b.Decl(b.Var(result, result_ty));
-                        hoist_to_decl_before.Prepare(sem_call);
-                        hoist_to_decl_before.InsertBefore(stmt, result_decl);
-                        hoist_to_decl_before.InsertBefore(stmt, masked_call);
-                        ctx.Replace(call, b.Expr(result));
-                    }
-                }
-            },
-
-            // Insert a conditional discard before all return statements in entry points.
-            [&](const ReturnStatement* ret) {
-                auto* func = sem.Get(ret)->Function();
-                if (func->Declaration()->IsEntryPoint() && functions_to_process.count(func)) {
-                    auto* discard = b.If(flag, b.Block(b.Discard()));
-                    ctx.InsertBefore(sem.Get(ret)->Block()->Declaration()->statements, ret,
-                                     discard);
-                }
-            });
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/demote_to_helper.h b/src/tint/lang/wgsl/ast/transform/demote_to_helper.h
deleted file mode 100644
index fcc1b99..0000000
--- a/src/tint/lang/wgsl/ast/transform/demote_to_helper.h
+++ /dev/null
@@ -1,60 +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_WGSL_AST_TRANSFORM_DEMOTE_TO_HELPER_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_DEMOTE_TO_HELPER_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Implement demote-to-helper semantics for discard statements.
-///
-/// For backend targets that implement discard by terminating the invocation, we need to change the
-/// program to ensure that discarding the fragment does not affect uniformity with respect to
-/// derivative operations. We do this by setting a global flag and masking all writes to storage
-/// buffers and textures.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * PromoteSideEffectsToDecl
-/// * ExpandCompoundAssignment
-class DemoteToHelper final : public Castable<DemoteToHelper, Transform> {
-  public:
-    /// Constructor
-    DemoteToHelper();
-    /// Destructor
-    ~DemoteToHelper() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_DEMOTE_TO_HELPER_H_
diff --git a/src/tint/lang/wgsl/ast/transform/demote_to_helper_test.cc b/src/tint/lang/wgsl/ast/transform/demote_to_helper_test.cc
deleted file mode 100644
index ce447c0..0000000
--- a/src/tint/lang/wgsl/ast/transform/demote_to_helper_test.cc
+++ /dev/null
@@ -1,1399 +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/wgsl/ast/transform/demote_to_helper.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using DemoteToHelperTest = TransformTest;
-
-TEST_F(DemoteToHelperTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<DemoteToHelper>(src));
-}
-
-TEST_F(DemoteToHelperTest, ShouldRunNoDiscard) {
-    auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> v : f32;
-
-@fragment
-fn foo() {
-  v = 42;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<DemoteToHelper>(src));
-}
-
-TEST_F(DemoteToHelperTest, ShouldRunDiscardInEntryPoint) {
-    auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> v : f32;
-
-@fragment
-fn foo() {
-  discard;
-  v = 42;
-}
-)";
-
-    EXPECT_TRUE(ShouldRun<DemoteToHelper>(src));
-}
-
-TEST_F(DemoteToHelperTest, ShouldRunDiscardInHelper) {
-    auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> v : f32;
-
-fn bar() {
-  discard;
-}
-
-@fragment
-fn foo() {
-  bar();
-  v = 42;
-}
-)";
-
-    EXPECT_TRUE(ShouldRun<DemoteToHelper>(src));
-}
-
-TEST_F(DemoteToHelperTest, EmptyModule) {
-    auto* src = R"()";
-
-    auto* expect = src;
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInEntryPoint_DiscardInEntryPoint) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  v = ret.x;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  if (!(tint_discarded)) {
-    v = ret.x;
-  }
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInEntryPoint_DiscardInHelper) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
-  discard;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    bar();
-  }
-  let ret = textureSample(t, s, coord);
-  v = ret.x;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
-  tint_discarded = true;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    bar();
-  }
-  let ret = textureSample(t, s, coord);
-  if (!(tint_discarded)) {
-    v = ret.x;
-  }
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInHelper_DiscardInEntryPoint) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar(coord : vec2<f32>) {
-  let ret = textureSample(t, s, coord);
-  v = ret.x;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  bar(coord);
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar(coord : vec2<f32>) {
-  let ret = textureSample(t, s, coord);
-  if (!(tint_discarded)) {
-    v = ret.x;
-  }
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  bar(coord);
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInHelper_DiscardInHelper) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar(in : f32, coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  v = ret.x;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  bar(in, coord);
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar(in : f32, coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  if (!(tint_discarded)) {
-    v = ret.x;
-  }
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  bar(in, coord);
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, WriteInEntryPoint_NoDiscard) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  let ret = textureSample(t, s, coord);
-  v = ret.x;
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that write via sugared pointer also discards
-TEST_F(DemoteToHelperTest, WriteInEntryPoint_DiscardInEntryPoint_ViaPointerDot) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : vec4<f32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  let p = &v;
-  p.x = ret.x;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : vec4<f32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  let p = &(v);
-  if (!(tint_discarded)) {
-    p.x = ret.x;
-  }
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that no additional discards are inserted when the function unconditionally returns in a
-// nested block.
-TEST_F(DemoteToHelperTest, EntryPointReturn_NestedInBlock) {
-    auto* src = R"(
-@fragment
-fn foo() {
-  {
-    discard;
-    return;
-  }
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@fragment
-fn foo() {
-  {
-    tint_discarded = true;
-    if (tint_discarded) {
-      discard;
-    }
-    return;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that a discard statement is inserted before every return statement in an entry point that
-// contains a discard.
-TEST_F(DemoteToHelperTest, EntryPointReturns_DiscardInEntryPoint) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  if (in < 1.0) {
-    return ret.x;
-  }
-  return 2.0;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  if ((in < 1.0)) {
-    if (tint_discarded) {
-      discard;
-    }
-    return ret.x;
-  }
-  if (tint_discarded) {
-    discard;
-  }
-  return 2.0;
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that a discard statement is inserted before every return statement in an entry point that
-// calls a function that contains a discard.
-TEST_F(DemoteToHelperTest, EntryPointReturns_DiscardInHelper) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
-  discard;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
-  if (in == 0.0) {
-    bar();
-  }
-  let ret = textureSample(t, s, coord);
-  if (in < 1.0) {
-    return ret.x;
-  }
-  return 2.0;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
-  tint_discarded = true;
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
-  if ((in == 0.0)) {
-    bar();
-  }
-  let ret = textureSample(t, s, coord);
-  if ((in < 1.0)) {
-    if (tint_discarded) {
-      discard;
-    }
-    return ret.x;
-  }
-  if (tint_discarded) {
-    discard;
-  }
-  return 2.0;
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that no return statements are modified in an entry point that does not discard.
-TEST_F(DemoteToHelperTest, EntryPointReturns_NoDiscard) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-fn bar() {
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) f32 {
-  if ((in == 0.0)) {
-    bar();
-  }
-  let ret = textureSample(t, s, coord);
-  if ((in < 1.0)) {
-    return ret.x;
-  }
-  return 2.0;
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that only functions that are part of a shader that discards are transformed.
-// Functions in non-discarding stages should not have their writes masked, and non-discarding entry
-// points should not have their return statements replaced.
-TEST_F(DemoteToHelperTest, MultipleShaders) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v1 : f32;
-
-@group(0) @binding(3) var<storage, read_write> v2 : f32;
-
-fn bar_discard(in : f32, coord : vec2<f32>) -> f32 {
-  let ret = textureSample(t, s, coord);
-  v1 = ret.x * 2.0;
-  return ret.y * 2.0;
-}
-
-fn bar_no_discard(in : f32, coord : vec2<f32>) -> f32 {
-  let ret = textureSample(t, s, coord);
-  v1 = ret.x * 2.0;
-  return ret.y * 2.0;
-}
-
-@fragment
-fn foo_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = bar_discard(in, coord);
-  v2 = ret;
-}
-
-@fragment
-fn foo_no_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  let ret = bar_no_discard(in, coord);
-  if (in == 0.0) {
-    return;
-  }
-  v2 = ret;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v1 : f32;
-
-@group(0) @binding(3) var<storage, read_write> v2 : f32;
-
-fn bar_discard(in : f32, coord : vec2<f32>) -> f32 {
-  let ret = textureSample(t, s, coord);
-  if (!(tint_discarded)) {
-    v1 = (ret.x * 2.0);
-  }
-  return (ret.y * 2.0);
-}
-
-fn bar_no_discard(in : f32, coord : vec2<f32>) -> f32 {
-  let ret = textureSample(t, s, coord);
-  v1 = (ret.x * 2.0);
-  return (ret.y * 2.0);
-}
-
-@fragment
-fn foo_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = bar_discard(in, coord);
-  if (!(tint_discarded)) {
-    v2 = ret;
-  }
-  if (tint_discarded) {
-    discard;
-  }
-}
-
-@fragment
-fn foo_no_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  let ret = bar_no_discard(in, coord);
-  if ((in == 0.0)) {
-    return;
-  }
-  v2 = ret;
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that we do not mask writes to invocation-private address spaces.
-TEST_F(DemoteToHelperTest, InvocationPrivateWrites) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-var<private> vp : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  var vf : f32;
-  vf = ret.x;
-  vp = ret.y;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-var<private> vp : f32;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  var vf : f32;
-  vf = ret.x;
-  vp = ret.y;
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that we do not mask writes to invocation-private address spaces via a sugared pointer write
-TEST_F(DemoteToHelperTest, InvocationPrivateWritesViaPointerDot) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-var<private> vp : vec4<f32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  var vf : f32;
-  vf = ret.x;
-  let p = &vp;
-  p.x = ret.x;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-var<private> vp : vec4<f32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  var vf : f32;
-  vf = ret.x;
-  let p = &(vp);
-  p.x = ret.x;
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, TextureStoreInEntryPoint) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  textureStore(t2, vec2<u32>(coord), ret);
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  if (!(tint_discarded)) {
-    textureStore(t2, vec2<u32>(coord), ret);
-  }
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, TextureStoreInHelper) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-fn bar(coord : vec2<f32>, value : vec4<f32>) {
-  textureStore(t2, vec2<u32>(coord), value);
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  bar(coord, ret);
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-fn bar(coord : vec2<f32>, value : vec4<f32>) {
-  if (!(tint_discarded)) {
-    textureStore(t2, vec2<u32>(coord), value);
-  }
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  bar(coord, ret);
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, TextureStore_NoDiscard) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var t2 : texture_storage_2d<rgba8unorm, write>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  let ret = textureSample(t, s, coord);
-  textureStore(t2, vec2<u32>(coord), ret);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicStoreInEntryPoint) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  atomicStore(&a, i32(ret.x));
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  if (!(tint_discarded)) {
-    atomicStore(&(a), i32(ret.x));
-  }
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicStoreInHelper) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-fn bar(value : vec4<f32>) {
-  atomicStore(&a, i32(value.x));
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if (in == 0.0) {
-    discard;
-  }
-  let ret = textureSample(t, s, coord);
-  bar(ret);
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-fn bar(value : vec4<f32>) {
-  if (!(tint_discarded)) {
-    atomicStore(&(a), i32(value.x));
-  }
-}
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let ret = textureSample(t, s, coord);
-  bar(ret);
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicStore_NoDiscard) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) {
-  let ret = textureSample(t, s, coord);
-  atomicStore(&(a), i32(ret.x));
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicBuiltinExpression) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
-  if (in == 0.0) {
-    discard;
-  }
-  let v = i32(textureSample(t, s, coord).x);
-  let result = v + atomicAdd(&a, v);
-  return result;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let v = i32(textureSample(t, s, coord).x);
-  var tint_symbol : i32;
-  if (!(tint_discarded)) {
-    tint_symbol = atomicAdd(&(a), v);
-  }
-  let result = (v + tint_symbol);
-  if (tint_discarded) {
-    discard;
-  }
-  return result;
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicBuiltinExpression_InForLoopContinuing) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
-  if (in == 0.0) {
-    discard;
-  }
-  var result = 0;
-  for (var i = 0; i < 10; i = atomicAdd(&a, 1)) {
-    result += i;
-  }
-  result += i32(textureSample(t, s, coord).x);
-  return result;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  var result = 0;
-  {
-    var i = 0;
-    loop {
-      if (!((i < 10))) {
-        break;
-      }
-      {
-        result += i;
-      }
-
-      continuing {
-        var tint_symbol : i32;
-        if (!(tint_discarded)) {
-          tint_symbol = atomicAdd(&(a), 1);
-        }
-        i = tint_symbol;
-      }
-    }
-  }
-  result += i32(textureSample(t, s, coord).x);
-  if (tint_discarded) {
-    discard;
-  }
-  return result;
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, AtomicCompareExchangeWeak) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
-  if (in == 0.0) {
-    discard;
-  }
-  var result = 0;
-  if (!atomicCompareExchangeWeak(&a, i32(in), 42).exchanged) {
-    let xchg = atomicCompareExchangeWeak(&a, i32(in), 42);
-    result = xchg.old_value;
-  }
-  result += i32(textureSample(t, s, coord).x);
-  return result;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  var result = 0;
-  var tint_symbol : __atomic_compare_exchange_result_i32;
-  if (!(tint_discarded)) {
-    tint_symbol = atomicCompareExchangeWeak(&(a), i32(in), 42);
-  }
-  if (!(tint_symbol.exchanged)) {
-    var tint_symbol_1 : __atomic_compare_exchange_result_i32;
-    if (!(tint_discarded)) {
-      tint_symbol_1 = atomicCompareExchangeWeak(&(a), i32(in), 42);
-    }
-    let xchg = tint_symbol_1;
-    result = xchg.old_value;
-  }
-  result += i32(textureSample(t, s, coord).x);
-  if (tint_discarded) {
-    discard;
-  }
-  return result;
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that no masking is generated for calls to `atomicLoad()`.
-TEST_F(DemoteToHelperTest, AtomicLoad) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
-  if (in == 0.0) {
-    discard;
-  }
-  let v = i32(textureSample(t, s, coord).x);
-  let result = v + atomicLoad(&a);
-  return result;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-@group(0) @binding(2) var<storage, read_write> v : f32;
-
-@group(0) @binding(3) var<storage, read_write> a : atomic<i32>;
-
-@fragment
-fn foo(@location(0) in : f32, @location(1) coord : vec2<f32>) -> @location(0) i32 {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let v = i32(textureSample(t, s, coord).x);
-  let result = (v + atomicLoad(&(a)));
-  if (tint_discarded) {
-    discard;
-  }
-  return result;
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, PhonyAssignment) {
-    auto* src = R"(
-@fragment
-fn foo(@location(0) in : f32) {
-  if (in == 0.0) {
-    discard;
-  }
-  _ = in;
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@fragment
-fn foo(@location(0) in : f32) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  _ = in;
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, Assignment_HoistExplicitDerivative) {
-    auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> output : array<f32, 4>;
-
-@fragment
-fn foo(@location(0) in : f32) {
-  if (in == 0.0) {
-    discard;
-  }
-  output[u32(in)] = dpdx(in);
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var<storage, read_write> output : array<f32, 4>;
-
-@fragment
-fn foo(@location(0) in : f32) {
-  if ((in == 0.0)) {
-    tint_discarded = true;
-  }
-  let tint_symbol : f32 = dpdx(in);
-  if (!(tint_discarded)) {
-    output[u32(in)] = tint_symbol;
-  }
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DemoteToHelperTest, Assignment_HoistImplicitDerivative) {
-    auto* src = R"(
-@group(0) @binding(0)
-var<storage, read_write> output : array<vec4f, 4>;
-
-@group(0) @binding(1)
-var t : texture_2d<f32>;
-
-@group(0) @binding(2)
-var s : sampler;
-
-@fragment
-fn foo(@interpolate(flat) @location(0) in : u32) {
-  if (in == 0) {
-    discard;
-  }
-  output[in] = textureSample(t, s, vec2());
-}
-)";
-
-    auto* expect = R"(
-var<private> tint_discarded = false;
-
-@group(0) @binding(0) var<storage, read_write> output : array<vec4f, 4>;
-
-@group(0) @binding(1) var t : texture_2d<f32>;
-
-@group(0) @binding(2) var s : sampler;
-
-@fragment
-fn foo(@interpolate(flat) @location(0) in : u32) {
-  if ((in == 0)) {
-    tint_discarded = true;
-  }
-  let tint_symbol : vec4<f32> = textureSample(t, s, vec2());
-  if (!(tint_discarded)) {
-    output[in] = tint_symbol;
-  }
-  if (tint_discarded) {
-    discard;
-  }
-}
-)";
-
-    auto got = Run<DemoteToHelper>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc b/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
deleted file mode 100644
index 0918238..0000000
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access.cc
+++ /dev/null
@@ -1,1223 +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/wgsl/ast/transform/direct_variable_access.h"
-
-#include <algorithm>
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/abstract_int.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/ast/traverse_expressions.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/index_accessor_expression.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/statement.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/reverse.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-#include "src/tint/utils/text/string_stream.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::DirectVariableAccess);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::DirectVariableAccess::Config);
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::ast::transform {
-
-namespace {
-
-/// AccessRoot describes the root of an AccessShape.
-struct AccessRoot {
-    /// The pointer-unwrapped type of the *transformed* variable.
-    /// This may be different for pointers in 'private' and 'function' address space, as the pointer
-    /// parameter type is to the *base object* instead of the input pointer type.
-    tint::core::type::Type const* type = nullptr;
-    /// The originating module-scope variable ('private', 'storage', 'uniform', 'workgroup'),
-    /// function-scope variable ('function'), or pointer parameter in the source program.
-    tint::sem::Variable const* variable = nullptr;
-    /// The address space of the variable or pointer type.
-    tint::core::AddressSpace address_space = tint::core::AddressSpace::kUndefined;
-
-    /// @return a hash code for this object
-    tint::HashCode HashCode() const { return Hash(type, variable); }
-};
-
-/// Inequality operator for AccessRoot
-bool operator!=(const AccessRoot& a, const AccessRoot& b) {
-    return a.type != b.type || a.variable != b.variable;
-}
-
-/// DynamicIndex is used by DirectVariableAccess::State::AccessOp to indicate an array, matrix or
-/// vector index.
-struct DynamicIndex {
-    /// @return a hash code for this object
-    tint::HashCode HashCode() const { return 42 /* empty struct: any number will do */; }
-};
-
-/// Inequality operator for DynamicIndex
-bool operator!=(const DynamicIndex&, const DynamicIndex&) {
-    return false;  // empty struct: two DynamicIndex objects are always equal
-}
-
-/// AccessOp describes a single access in an access chain.
-/// The access is one of:
-/// Symbol        - a struct member access.
-/// DynamicIndex  - a runtime index on an array, matrix column, or vector element.
-using AccessOp = std::variant<tint::Symbol, DynamicIndex>;
-
-/// A vector of AccessOp. Describes the static "path" from a root variable to an element
-/// within the variable. Array accessors index expressions are held externally to the
-/// AccessShape, so AccessShape will be considered equal even if the array, matrix or vector
-/// index values differ.
-///
-/// For example, consider the following:
-///
-/// ```
-///           struct A {
-///               x : array<i32, 8>,
-///               y : u32,
-///           };
-///           struct B {
-///               x : i32,
-///               y : array<A, 4>
-///           };
-///           var<workgroup> C : B;
-/// ```
-///
-/// The following AccessShape would describe the following:
-///
-/// +==============================+===============+=================================+
-/// | AccessShape                  | Type          |  Expression                     |
-/// +==============================+===============+=================================+
-/// | [ Variable 'C', Symbol 'x' ] | i32           |  C.x                            |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y' ] | array<A, 4>   |  C.y                            |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y',  | A             |  C.y[dyn_idx[0]]                |
-/// |   DynamicIndex ]             |               |                                 |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y',  | array<i32, 8> |  C.y[dyn_idx[0]].x              |
-/// |   DynamicIndex, Symbol 'x' ] |               |                                 |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y',  | i32           |  C.y[dyn_idx[0]].x[dyn_idx[1]]  |
-/// |   DynamicIndex, Symbol 'x',  |               |                                 |
-/// |   DynamicIndex ]             |               |                                 |
-/// +------------------------------+---------------+---------------------------------+
-/// | [ Variable 'C', Symbol 'y',  | u32           |  C.y[dyn_idx[0]].y              |
-/// |   DynamicIndex, Symbol 'y' ] |               |                                 |
-/// +------------------------------+---------------+---------------------------------+
-///
-/// Where: `dyn_idx` is the AccessChain::dynamic_indices.
-struct AccessShape {
-    // The originating variable.
-    AccessRoot root;
-    /// The chain of access ops.
-    tint::Vector<AccessOp, 8> ops;
-
-    /// @returns the number of DynamicIndex operations in #ops.
-    uint32_t NumDynamicIndices() const {
-        uint32_t count = 0;
-        for (auto& op : ops) {
-            if (std::holds_alternative<DynamicIndex>(op)) {
-                count++;
-            }
-        }
-        return count;
-    }
-
-    /// @return a hash code for this object
-    tint::HashCode HashCode() const { return Hash(root, ops); }
-};
-
-/// Equality operator for AccessShape
-bool operator==(const AccessShape& a, const AccessShape& b) {
-    return !(a.root != b.root) && a.ops == b.ops;
-}
-
-/// Inequality operator for AccessShape
-bool operator!=(const AccessShape& a, const AccessShape& b) {
-    return !(a == b);
-}
-
-/// AccessChain describes a chain of access expressions originating from a variable.
-struct AccessChain : AccessShape {
-    /// The array accessor index expressions. This vector is indexed by the `DynamicIndex`s in
-    /// #indices.
-    Vector<const sem::ValueExpression*, 8> dynamic_indices;
-    /// If true, then this access chain is used as an argument to call a variant.
-    bool used_in_call = false;
-};
-
-}  // namespace
-
-/// The PIMPL state for the DirectVariableAccess transform
-struct DirectVariableAccess::State {
-    /// Constructor
-    /// @param src the source Program
-    /// @param options the transform options
-    State(const Program& src, const Options& options)
-        : ctx{&b, &src, /* auto_clone_symbols */ true}, opts(options) {}
-
-    /// The main function for the transform.
-    /// @returns the ApplyResult
-    ApplyResult Run() {
-        // If there are no functions with pointer parameters, then this transform can be skipped.
-        if (!AnyPointerParameters()) {
-            return SkipTransform;
-        }
-
-        // Stage 1:
-        // Walk all the expressions of the program, starting with the expression leaves.
-        // Whenever we find an identifier resolving to a var, pointer parameter or pointer let to
-        // another chain, start constructing an access chain. When chains are accessed, these chains
-        // are grown and moved up the expression tree. After this stage, we are left with all the
-        // expression access chains to variables that we may need to transform.
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
-            if (auto* expr = sem.GetVal(node)) {
-                AppendAccessChain(expr);
-            }
-        }
-
-        // Stage 2:
-        // Walk the functions in dependency order, starting with the entry points.
-        // Construct the set of function 'variants' by examining the calls made by each function to
-        // their call target. Each variant holds a map of pointer parameter to access chains, and
-        // will have the pointer parameters replaced with an array of u32s, used to perform the
-        // pointer indexing in the variant.
-        // Function call pointer arguments are replaced with an array of these dynamic indices.
-        auto decls = sem.Module()->DependencyOrderedDeclarations();
-        for (auto* decl : tint::Reverse(decls)) {
-            if (auto* fn = sem.Get<sem::Function>(decl)) {
-                auto* fn_info = FnInfoFor(fn);
-                ProcessFunction(fn, fn_info);
-                TransformFunction(fn, fn_info);
-            }
-        }
-
-        // Stage 3:
-        // Filter out access chains that do not need transforming.
-        // Ensure that chain dynamic index expressions are evaluated once at the correct place
-        ProcessAccessChains();
-
-        // Stage 4:
-        // Replace all the access chain expressions in all functions with reconstructed expression
-        // using the originating global variable, and any dynamic indices passed in to the function
-        // variant.
-        TransformAccessChainExpressions();
-
-        // Stage 5:
-        // Actually kick the clone.
-        CloneState state;
-        clone_state = &state;
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-  private:
-    /// Holds symbols of the transformed pointer parameter.
-    /// If both symbols are valid, then #base_ptr and #indices are both program-unique symbols
-    /// derived from the original parameter name.
-    /// If only one symbol is valid, then this is the original parameter symbol.
-    struct PtrParamSymbols {
-        /// The symbol of the base pointer parameter.
-        Symbol base_ptr;
-        /// The symbol of the dynamic indicies parameter.
-        Symbol indices;
-    };
-
-    /// FnVariant describes a unique variant of a function, specialized by the AccessShape of the
-    /// pointer arguments - also known as the variant's "signature".
-    ///
-    /// To help understand what a variant is, consider the following WGSL:
-    ///
-    /// ```
-    /// fn F(a : ptr<storage, u32>, b : u32, c : ptr<storage, u32>) {
-    ///    return *a + b + *c;
-    /// }
-    ///
-    /// @group(0) @binding(0) var<storage> S0 : u32;
-    /// @group(0) @binding(0) var<storage> S1 : array<u32, 64>;
-    ///
-    /// fn x() {
-    ///    F(&S0, 0, &S0);       // (A)
-    ///    F(&S0, 0, &S0);       // (B)
-    ///    F(&S1[0], 1, &S0);    // (C)
-    ///    F(&S1[5], 2, &S0);    // (D)
-    ///    F(&S1[5], 3, &S1[3]); // (E)
-    ///    F(&S1[7], 4, &S1[2]); // (F)
-    /// }
-    /// ```
-    ///
-    /// Given the calls in x(), function F() will have 3 variants:
-    /// (1) F<S0,S0>                   - called by (A) and (B).
-    ///                                  Note that only 'uniform', 'storage' and 'workgroup' pointer
-    ///                                  parameters are considered for a variant signature, and so
-    ///                                  the argument for parameter 'b' is not included in the
-    ///                                  signature.
-    /// (2) F<S1[dyn_idx],S0>          - called by (C) and (D).
-    ///                                  Note that the array index value is external to the
-    ///                                  AccessShape, and so is not part of the variant signature.
-    /// (3) F<S1[dyn_idx],S1[dyn_idx]> - called by (E) and (F).
-    ///
-    /// Each variant of the function will be emitted as a separate function by the transform, and
-    /// would look something like:
-    ///
-    /// ```
-    /// // variant F<S0,S0> (1)
-    /// fn F_S0_S0(b : u32) {
-    ///    return S0 + b + S0;
-    /// }
-    ///
-    /// type S1_X = array<u32, 1>;
-    ///
-    /// // variant F<S1[dyn_idx],S0> (2)
-    /// fn F_S1_X_S0(a : S1_X, b : u32) {
-    ///    return S1[a[0]] + b + S0;
-    /// }
-    ///
-    /// // variant F<S1[dyn_idx],S1[dyn_idx]> (3)
-    /// fn F_S1_X_S1_X(a : S1_X, b : u32, c : S1_X) {
-    ///    return S1[a[0]] + b + S1[c[0]];
-    /// }
-    ///
-    /// @group(0) @binding(0) var<storage> S0 : u32;
-    /// @group(0) @binding(0) var<storage> S1 : array<u32, 64>;
-    ///
-    /// fn x() {
-    ///    F_S0_S0(0);                        // (A)
-    ///    F(&S0, 0, &S0);                    // (B)
-    ///    F_S1_X_S0(S1_X(0), 1);             // (C)
-    ///    F_S1_X_S0(S1_X(5), 2);             // (D)
-    ///    F_S1_X_S1_X(S1_X(5), 3, S1_X(3));  // (E)
-    ///    F_S1_X_S1_X(S1_X(7), 4, S1_X(2));  // (F)
-    /// }
-    /// ```
-    struct FnVariant {
-        /// The signature of the variant is a map of each of the function's 'uniform', 'storage' and
-        /// 'workgroup' pointer parameters to the caller's AccessShape.
-        using Signature = Hashmap<const sem::Parameter*, AccessShape, 4>;
-
-        /// The unique name of the variant.
-        /// The symbol is in the `ctx.dst` program namespace.
-        Symbol name;
-
-        /// A map of direct calls made by this variant to the name of other function variants.
-        Hashmap<const sem::Call*, Symbol, 4> calls;
-
-        /// A map of input program parameter to output parameter symbols.
-        Hashmap<const sem::Parameter*, PtrParamSymbols, 4> ptr_param_symbols;
-
-        /// The declaration order of the variant, in relation to other variants of the same
-        /// function. Used to ensure deterministic ordering of the transform, as map iteration is
-        /// not deterministic between compilers.
-        size_t order = 0;
-    };
-
-    /// FnInfo holds information about a function in the input program.
-    struct FnInfo {
-        /// A map of variant signature to the variant data.
-        Hashmap<FnVariant::Signature, FnVariant, 8> variants;
-        /// A map of expressions that have been hoisted to a 'let' declaration in the function.
-        Hashmap<const sem::ValueExpression*, Symbol, 8> hoisted_exprs;
-
-        /// @returns the variants of the function in a deterministically ordered vector.
-        tint::Vector<std::pair<const FnVariant::Signature*, FnVariant*>, 8> SortedVariants() {
-            tint::Vector<std::pair<const FnVariant::Signature*, FnVariant*>, 8> out;
-            out.Reserve(variants.Count());
-            for (auto& it : variants) {
-                out.Push({&it.key.Value(), &it.value});
-            }
-            out.Sort([&](auto& va, auto& vb) { return va.second->order < vb.second->order; });
-            return out;
-        }
-    };
-
-    /// The program builder
-    ProgramBuilder b;
-    /// The clone context
-    program::CloneContext ctx;
-    /// The transform options
-    const Options& opts;
-    /// Alias to the semantic info in ctx.src
-    const sem::Info& sem = ctx.src->Sem();
-    /// Alias to the symbols in ctx.src
-    const SymbolTable& sym = ctx.src->Symbols();
-    /// Map of semantic function to the function info
-    Hashmap<const sem::Function*, FnInfo*, 8> fns;
-    /// Map of AccessShape to the name of a type alias for the an array<u32, N> used for the
-    /// dynamic indices of an access chain, passed down as the transformed type of a variant's
-    /// pointer parameter.
-    Hashmap<AccessShape, Symbol, 8> dynamic_index_array_aliases;
-    /// Map of semantic expression to AccessChain
-    Hashmap<const sem::ValueExpression*, AccessChain*, 32> access_chains;
-    /// Allocator for FnInfo
-    BlockAllocator<FnInfo> fn_info_allocator;
-    /// Allocator for AccessChain
-    BlockAllocator<AccessChain> access_chain_allocator;
-    /// Helper used for hoisting expressions to lets
-    HoistToDeclBefore hoist{ctx};
-    /// Map of string to unique symbol (no collisions in output program).
-    Hashmap<std::string, Symbol, 8> unique_symbols;
-
-    /// CloneState holds pointers to the current function, variant and variant's parameters.
-    struct CloneState {
-        /// The current function being cloned
-        FnInfo* current_function = nullptr;
-        /// The current function variant being built
-        FnVariant* current_variant = nullptr;
-        /// The signature of the current function variant being built
-        const FnVariant::Signature* current_variant_sig = nullptr;
-    };
-
-    /// The clone state.
-    /// Only valid during the lifetime of the program::CloneContext::Clone().
-    CloneState* clone_state = nullptr;
-
-    /// @returns true if any user functions have parameters of a pointer type.
-    bool AnyPointerParameters() const {
-        for (auto* fn : ctx.src->AST().Functions()) {
-            for (auto* param : fn->params) {
-                if (sem.Get(param)->Type()->Is<core::type::Pointer>()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /// AppendAccessChain creates or extends an existing AccessChain for the given expression,
-    /// modifying the #access_chains map.
-    void AppendAccessChain(const sem::ValueExpression* expr) {
-        // take_chain moves the AccessChain from the expression `from` to the expression `expr`.
-        // Returns nullptr if `from` did not hold an access chain.
-        auto take_chain = [&](const sem::ValueExpression* from) -> AccessChain* {
-            if (auto* chain = AccessChainFor(from)) {
-                access_chains.Remove(from);
-                access_chains.Add(expr, chain);
-                return chain;
-            }
-            return nullptr;
-        };
-
-        Switch(
-            expr,
-            [&](const sem::VariableUser* user) {
-                // Expression resolves to a variable.
-                auto* variable = user->Variable();
-
-                auto create_new_chain = [&] {
-                    auto* chain = access_chain_allocator.Create();
-                    chain->root.variable = variable;
-                    chain->root.type = variable->Type();
-                    chain->root.address_space = variable->AddressSpace();
-                    if (auto* ptr = chain->root.type->As<core::type::Pointer>()) {
-                        chain->root.address_space = ptr->AddressSpace();
-                    }
-                    access_chains.Add(expr, chain);
-                };
-
-                Switch(
-                    variable->Declaration(),
-                    [&](const Var*) {
-                        if (variable->AddressSpace() != core::AddressSpace::kHandle) {
-                            // Start a new access chain for the non-handle 'var' access
-                            create_new_chain();
-                        }
-                    },
-                    [&](const Parameter*) {
-                        if (variable->Type()->Is<core::type::Pointer>()) {
-                            // Start a new access chain for the pointer parameter access
-                            create_new_chain();
-                        }
-                    },
-                    [&](const Let*) {
-                        if (variable->Type()->Is<core::type::Pointer>()) {
-                            // variable is a pointer-let.
-                            auto* init = sem.GetVal(variable->Declaration()->initializer);
-                            // Note: We do not use take_chain() here, as we need to preserve the
-                            // AccessChain on the let's initializer, as the let needs its
-                            // initializer updated, and the let may be used multiple times. Instead
-                            // we copy the let's AccessChain into a a new AccessChain.
-                            if (auto* init_chain = AccessChainFor(init)) {
-                                access_chains.Add(expr, access_chain_allocator.Create(*init_chain));
-                            }
-                        }
-                    });
-            },
-            [&](const sem::StructMemberAccess* a) {
-                // Structure member access.
-                // Append the Symbol of the member name to the chain, and move the chain to the
-                // member access expression.
-                if (auto* chain = take_chain(a->Object())) {
-                    chain->ops.Push(a->Member()->Name());
-                }
-            },
-            [&](const sem::IndexAccessorExpression* a) {
-                // Array, matrix or vector index.
-                // Store the index expression into AccessChain::dynamic_indices, append a
-                // DynamicIndex to the chain, and move the chain to the index accessor expression.
-                if (auto* chain = take_chain(a->Object())) {
-                    chain->ops.Push(DynamicIndex{});
-                    chain->dynamic_indices.Push(a->Index());
-                }
-            },
-            [&](const sem::ValueExpression* e) {
-                if (auto* unary = e->Declaration()->As<UnaryOpExpression>()) {
-                    // Unary op.
-                    // If this is a '&' or '*', simply move the chain to the unary op expression.
-                    if (unary->op == core::UnaryOp::kAddressOf ||
-                        unary->op == core::UnaryOp::kIndirection) {
-                        take_chain(sem.GetVal(unary->expr));
-                    }
-                }
-            });
-    }
-
-    /// MaybeHoistDynamicIndices examines the AccessChain::dynamic_indices member of @p chain,
-    /// hoisting all expressions to their own uniquely named 'let' if none of the following are
-    /// true:
-    /// 1. The index expression is a constant value.
-    /// 2. The index expression's statement is the same as @p usage.
-    /// 3. The index expression is an identifier resolving to a 'let', 'const' or parameter, AND
-    ///    that identifier resolves to the same variable at @p usage.
-    ///
-    /// A dynamic index will only be hoisted once. The hoisting applies to all variants of the
-    /// function that holds the dynamic index expression.
-    void MaybeHoistDynamicIndices(AccessChain* chain, const sem::Statement* usage) {
-        for (auto& idx : chain->dynamic_indices) {
-            if (idx->ConstantValue()) {
-                // Dynamic index is constant.
-                continue;  // Hoisting not required.
-            }
-
-            if (idx->Stmt() == usage) {
-                // The index expression is owned by the statement of usage.
-                continue;  // Hoisting not required
-            }
-
-            if (auto* idx_variable_user = idx->UnwrapMaterialize()->As<sem::VariableUser>()) {
-                auto* idx_variable = idx_variable_user->Variable();
-                if (idx_variable->Declaration()->IsAnyOf<Let, Parameter>()) {
-                    // Dynamic index is an immutable variable
-                    continue;  // Hoisting not required.
-                }
-            }
-
-            // The dynamic index needs to be hoisted (if it hasn't been already).
-            auto fn = FnInfoFor(idx->Stmt()->Function());
-            fn->hoisted_exprs.GetOrAdd(idx, [this, idx] {
-                // Create a name for the new 'let'
-                auto name = b.Symbols().New("ptr_index_save");
-                // Insert a new 'let' just above the dynamic index statement.
-                hoist.InsertBefore(idx->Stmt(), [this, idx, name] {
-                    return b.Decl(b.Let(name, ctx.CloneWithoutTransform(idx->Declaration())));
-                });
-                return name;
-            });
-        }
-    }
-
-    /// BuildDynamicIndex builds the AST expression node for the dynamic index expression used in an
-    /// AccessChain. This is similar to just cloning the expression, but BuildDynamicIndex()
-    /// also:
-    /// * Collapses constant value index expressions down to the computed value. This acts as an
-    ///   constant folding optimization and reduces noise from the transform.
-    /// * Casts the resulting expression to a u32 if @p cast_to_u32 is true, and the expression type
-    ///   isn't implicitly usable as a u32. This is to help feed the expression into a
-    ///   `array<u32, N>` argument passed to a callee variant function.
-    const Expression* BuildDynamicIndex(const sem::ValueExpression* idx, bool cast_to_u32) {
-        if (auto* val = idx->ConstantValue()) {
-            // Expression evaluated to a constant value. Just emit that constant.
-            return b.Expr(val->ValueAs<AInt>());
-        }
-
-        // Expression is not a constant, clone the expression.
-        // Note: If the dynamic index expression was hoisted to a let, then cloning will return an
-        // identifier expression to the hoisted let.
-        auto* expr = ctx.Clone(idx->Declaration());
-
-        if (cast_to_u32) {
-            // The index may be fed to a dynamic index array<u32, N> argument, so the index
-            // expression may need casting to u32.
-            if (!idx->UnwrapMaterialize()
-                     ->Type()
-                     ->UnwrapRef()
-                     ->IsAnyOf<core::type::U32, core::type::AbstractInt>()) {
-                expr = b.Call<u32>(expr);
-            }
-        }
-
-        return expr;
-    }
-
-    /// ProcessFunction scans the direct calls made by the function @p fn, adding new variants to
-    /// the callee functions and transforming the call expression to pass dynamic indices instead of
-    /// true pointers.
-    /// If the function @p fn has pointer parameters that must be transformed to a caller variant,
-    /// and the function is not called, then the function is dropped from the output of the
-    /// transform, as it cannot be generated.
-    /// @note ProcessFunction must be called in dependency order for the program, starting with the
-    /// entry points.
-    void ProcessFunction(const sem::Function* fn, FnInfo* fn_info) {
-        if (fn_info->variants.IsEmpty()) {
-            // Function has no variants pre-generated by callers.
-            if (MustBeCalled(fn)) {
-                // Drop the function, as it wasn't called and cannot be generated.
-                ctx.Remove(ctx.src->AST().GlobalDeclarations(), fn->Declaration());
-                return;
-            }
-
-            // Function was not called. Create a single variant with an empty signature.
-            FnVariant variant;
-            variant.name = ctx.Clone(fn->Declaration()->name->symbol);
-            variant.order = 0;  // Unaltered comes first.
-            fn_info->variants.Add(FnVariant::Signature{}, std::move(variant));
-        }
-
-        // Process each of the direct calls made by this function.
-        for (auto* call : fn->DirectCalls()) {
-            ProcessCall(fn_info, call);
-        }
-    }
-
-    /// ProcessCall creates new variants of the callee function by permuting the call for each of
-    /// the variants of @p caller. ProcessCall also registers the clone callback to transform the
-    /// call expression to pass dynamic indices instead of true pointers.
-    void ProcessCall(FnInfo* caller, const sem::Call* call) {
-        auto* target = call->Target()->As<sem::Function>();
-        if (!target) {
-            // Call target is not a user-declared function.
-            return;  // Not interested in this call.
-        }
-
-        if (!HasPointerParameter(target)) {
-            return;  // Not interested in this call.
-        }
-
-        bool call_needs_transforming = false;
-
-        // Build the call target function variant for each variant of the caller.
-        for (auto caller_variant_it : caller->SortedVariants()) {
-            auto& caller_signature = *caller_variant_it.first;
-            auto& caller_variant = *caller_variant_it.second;
-
-            // Build the target variant's signature.
-            FnVariant::Signature target_signature;
-            for (size_t i = 0; i < call->Arguments().Length(); i++) {
-                const auto* arg = call->Arguments()[i];
-                const auto* param = target->Parameters()[i];
-                const auto* param_ty = param->Type()->As<core::type::Pointer>();
-                if (!param_ty) {
-                    continue;  // Parameter type is not a pointer.
-                }
-
-                // Fetch the access chain for the argument.
-                auto* arg_chain = AccessChainFor(arg);
-                if (!arg_chain) {
-                    continue;  // Argument does not have an access chain
-                }
-
-                // Construct the absolute AccessShape by considering the AccessShape of the caller
-                // variant's argument. This will propagate back through pointer parameters, to the
-                // outermost caller.
-                auto absolute = AbsoluteAccessShape(caller_signature, *arg_chain);
-
-                // If the address space of the root variable of the access chain does not require
-                // transformation, then there's nothing to do.
-                if (!AddressSpaceRequiresTransform(absolute.root.address_space)) {
-                    continue;
-                }
-
-                // Record that this chain was used in a function call.
-                // This preserves the chain during the access chain filtering stage.
-                arg_chain->used_in_call = true;
-
-                if (IsPrivateOrFunction(absolute.root.address_space)) {
-                    // Pointers in 'private' and 'function' address spaces need to be passed by
-                    // pointer argument.
-                    absolute.root.variable = param;
-                }
-
-                // Add the parameter's absolute AccessShape to the target's signature.
-                target_signature.Add(param, std::move(absolute));
-            }
-
-            // Construct a new FnVariant if this is the first caller of the target signature
-            auto* target_info = FnInfoFor(target);
-            auto& target_variant = target_info->variants.GetOrAdd(target_signature, [&] {
-                if (target_signature.IsEmpty()) {
-                    // Call target does not require any argument changes.
-                    FnVariant variant;
-                    variant.name = ctx.Clone(target->Declaration()->name->symbol);
-                    variant.order = 0;  // Unaltered comes first.
-                    return variant;
-                }
-
-                // Build an appropriate variant function name.
-                // This is derived from the original function name and the pointer parameter
-                // chains.
-                StringStream ss;
-                ss << target->Declaration()->name->symbol.Name();
-                for (auto* param : target->Parameters()) {
-                    if (auto indices = target_signature.Get(param)) {
-                        ss << "_" << AccessShapeName(*indices);
-                    }
-                }
-
-                // Build the pointer parameter symbols.
-                Hashmap<const sem::Parameter*, PtrParamSymbols, 4> ptr_param_symbols;
-                for (auto& param_it : target_signature) {
-                    auto* param = param_it.key.Value();
-                    auto& shape = param_it.value;
-
-                    // Parameter needs replacing with either zero, one or two parameters:
-                    // If the parameter is in the 'private' or 'function' address space, then the
-                    // originating pointer is always passed down. This always comes first.
-                    // If the access chain has dynamic indices, then we create an array<u32, N>
-                    // parameter to hold the dynamic indices.
-                    bool requires_base_ptr_param = IsPrivateOrFunction(shape.root.address_space);
-                    bool requires_indices_param = shape.NumDynamicIndices() > 0;
-
-                    PtrParamSymbols symbols;
-                    if (requires_base_ptr_param && requires_indices_param) {
-                        auto original_name = param->Declaration()->name->symbol;
-                        symbols.base_ptr = UniqueSymbolWithSuffix(original_name, "_base");
-                        symbols.indices = UniqueSymbolWithSuffix(original_name, "_indices");
-                    } else if (requires_base_ptr_param) {
-                        symbols.base_ptr = ctx.Clone(param->Declaration()->name->symbol);
-                    } else if (requires_indices_param) {
-                        symbols.indices = ctx.Clone(param->Declaration()->name->symbol);
-                    }
-
-                    // Remember this base pointer name.
-                    ptr_param_symbols.Add(param, symbols);
-                }
-
-                // Build the variant.
-                FnVariant variant;
-                variant.name = b.Symbols().New(ss.str());
-                variant.order = target_info->variants.Count() + 1;
-                variant.ptr_param_symbols = std::move(ptr_param_symbols);
-                return variant;
-            });
-
-            // Record the call made by caller variant to the target variant.
-            caller_variant.calls.Add(call, target_variant.name);
-            if (!target_signature.IsEmpty()) {
-                // The call expression will need transforming for at least one caller variant.
-                call_needs_transforming = true;
-            }
-        }
-
-        if (call_needs_transforming) {
-            // Register the clone callback to correctly transform the call expression into the
-            // appropriate variant calls.
-            TransformCall(call);
-        }
-    }
-
-    /// @returns true if the address space @p address_space requires transforming given the
-    /// transform's options.
-    bool AddressSpaceRequiresTransform(core::AddressSpace address_space) const {
-        switch (address_space) {
-            case core::AddressSpace::kUniform:
-            case core::AddressSpace::kStorage:
-            case core::AddressSpace::kWorkgroup:
-                return true;
-            case core::AddressSpace::kPrivate:
-                return opts.transform_private;
-            case core::AddressSpace::kFunction:
-                return opts.transform_function;
-            default:
-                return false;
-        }
-    }
-
-    /// @returns the AccessChain for the expression @p expr, or nullptr if the expression does
-    /// not hold an access chain.
-    AccessChain* AccessChainFor(const sem::ValueExpression* expr) const {
-        if (auto chain = access_chains.Get(expr)) {
-            return *chain;
-        }
-        return nullptr;
-    }
-
-    /// @returns the absolute AccessShape for @p indices, by replacing the originating pointer
-    /// parameter with the AccessChain of variant's signature.
-    AccessShape AbsoluteAccessShape(const FnVariant::Signature& signature,
-                                    const AccessShape& shape) const {
-        if (auto* root_param = shape.root.variable->As<sem::Parameter>()) {
-            if (auto incoming_chain = signature.Get(root_param)) {
-                // Access chain originates from a parameter, which will be transformed into an array
-                // of dynamic indices. Concatenate the signature's AccessShape for the parameter
-                // to the chain's indices, skipping over the chain's initial parameter index.
-                auto absolute = *incoming_chain;
-                for (auto& op : shape.ops) {
-                    absolute.ops.Push(op);
-                }
-                return absolute;
-            }
-        }
-
-        // Chain does not originate from a parameter, so is already absolute.
-        return shape;
-    }
-
-    /// TransformFunction registers the clone callback to transform the function @p fn into the
-    /// (potentially multiple) function's variants. TransformFunction will assign the current
-    /// function and variant to #clone_state, which can be used by the other clone callbacks.
-    void TransformFunction(const sem::Function* fn, FnInfo* fn_info) {
-        // Register a custom handler for the specific function
-        ctx.Replace(fn->Declaration(), [this, fn, fn_info] {
-            // For the scope of this lambda, assign current_function to fn_info.
-            TINT_SCOPED_ASSIGNMENT(clone_state->current_function, fn_info);
-
-            // This callback expects a single function returned. As we're generating potentially
-            // many variant functions, keep a record of the last created variant, and explicitly add
-            // this to the module if it isn't the last. We'll return the last created variant,
-            // taking the place of the original function.
-            const Function* pending_variant = nullptr;
-
-            // For each variant of fn...
-            for (auto variant_it : fn_info->SortedVariants()) {
-                if (pending_variant) {
-                    b.AST().AddFunction(pending_variant);
-                }
-
-                auto& variant_sig = *variant_it.first;
-                auto& variant = *variant_it.second;
-
-                // For the rest of this scope, assign the current variant and variant signature.
-                TINT_SCOPED_ASSIGNMENT(clone_state->current_variant_sig, &variant_sig);
-                TINT_SCOPED_ASSIGNMENT(clone_state->current_variant, &variant);
-
-                // Build the variant's parameters.
-                // Pointer parameters in the 'uniform', 'storage' or 'workgroup' address space are
-                // either replaced with an array of dynamic indices, or are dropped (if there are no
-                // dynamic indices).
-                tint::Vector<const Parameter*, 8> params;
-                for (auto* param : fn->Parameters()) {
-                    if (auto incoming_shape = variant_sig.Get(param)) {
-                        auto& symbols = *variant.ptr_param_symbols.Get(param);
-                        if (symbols.base_ptr.IsValid()) {
-                            auto base_ptr_ty = b.ty.ptr(
-                                incoming_shape->root.address_space,
-                                CreateASTTypeFor(ctx, incoming_shape->root.type->UnwrapPtrOrRef()));
-                            params.Push(b.Param(symbols.base_ptr, base_ptr_ty));
-                        }
-                        if (symbols.indices.IsValid()) {
-                            // Variant has dynamic indices for this variant, replace it.
-                            auto dyn_idx_arr_type = DynamicIndexArrayType(*incoming_shape);
-                            params.Push(b.Param(symbols.indices, dyn_idx_arr_type));
-                        }
-                    } else {
-                        // Just a regular parameter. Just clone the original parameter.
-                        params.Push(ctx.Clone(param->Declaration()));
-                    }
-                }
-
-                // Build the variant by cloning the source function. The other clone callbacks will
-                // use clone_state->current_variant and clone_state->current_variant_sig to produce
-                // the variant.
-                auto ret_ty = ctx.Clone(fn->Declaration()->return_type);
-                auto body = ctx.Clone(fn->Declaration()->body);
-                auto attrs = ctx.Clone(fn->Declaration()->attributes);
-                auto ret_attrs = ctx.Clone(fn->Declaration()->return_type_attributes);
-                pending_variant =
-                    b.create<Function>(b.Ident(variant.name), std::move(params), ret_ty, body,
-                                       std::move(attrs), std::move(ret_attrs));
-            }
-
-            return pending_variant;
-        });
-    }
-
-    /// TransformCall registers the clone callback to transform the call expression @p call to call
-    /// the correct target variant, and to replace pointers arguments with an array of dynamic
-    /// indices.
-    void TransformCall(const sem::Call* call) {
-        // Register a custom handler for the specific call expression
-        ctx.Replace(call->Declaration(), [this, call] {
-            auto target_variant = clone_state->current_variant->calls.Get(call);
-            if (!target_variant) {
-                // The current variant does not need to transform this call.
-                return ctx.CloneWithoutTransform(call->Declaration());
-            }
-
-            // Build the new call expressions's arguments.
-            tint::Vector<const Expression*, 8> new_args;
-            for (size_t arg_idx = 0; arg_idx < call->Arguments().Length(); arg_idx++) {
-                auto* arg = call->Arguments()[arg_idx];
-                auto* param = call->Target()->Parameters()[arg_idx];
-                auto* param_ty = param->Type()->As<core::type::Pointer>();
-                if (!param_ty) {
-                    // Parameter is not a pointer.
-                    // Just clone the unaltered argument.
-                    new_args.Push(ctx.Clone(arg->Declaration()));
-                    continue;  // Parameter is not a pointer
-                }
-
-                auto* chain = AccessChainFor(arg);
-                if (!chain) {
-                    // No access chain means the argument is not a pointer that needs transforming.
-                    // Just clone the unaltered argument.
-                    new_args.Push(ctx.Clone(arg->Declaration()));
-                    continue;
-                }
-
-                // Construct the absolute AccessShape by considering the AccessShape of the caller
-                // variant's argument. This will propagate back through pointer parameters, to the
-                // outermost caller.
-                auto full_indices = AbsoluteAccessShape(*clone_state->current_variant_sig, *chain);
-
-                // If the parameter is a pointer in the 'private' or 'function' address space, then
-                // we need to pass an additional pointer argument to the base object.
-                if (IsPrivateOrFunction(param_ty->AddressSpace())) {
-                    auto* root_expr = BuildAccessRootExpr(chain->root, /* deref */ false);
-                    if (!chain->root.variable->Is<sem::Parameter>()) {
-                        root_expr = b.AddressOf(root_expr);
-                    }
-                    new_args.Push(root_expr);
-                }
-
-                // Get or create the dynamic indices array.
-                if (auto dyn_idx_arr_ty = DynamicIndexArrayType(full_indices)) {
-                    // Build an array of dynamic indices to pass as the replacement for the pointer.
-                    tint::Vector<const Expression*, 8> dyn_idx_args;
-                    if (auto* root_param = chain->root.variable->As<sem::Parameter>()) {
-                        // Access chain originates from a pointer parameter.
-                        if (auto incoming_chain =
-                                clone_state->current_variant_sig->Get(root_param)) {
-                            auto indices =
-                                clone_state->current_variant->ptr_param_symbols.Get(root_param)
-                                    ->indices;
-
-                            // This pointer parameter will have been replaced with a array<u32, N>
-                            // holding the variant's dynamic indices for the pointer. Unpack these
-                            // directly into the array constructor's arguments.
-                            auto N = incoming_chain->NumDynamicIndices();
-                            for (uint32_t i = 0; i < N; i++) {
-                                dyn_idx_args.Push(b.IndexAccessor(indices, u32(i)));
-                            }
-                        }
-                    }
-                    // Pass the dynamic indices of the access chain into the array constructor.
-                    for (auto& dyn_idx : chain->dynamic_indices) {
-                        dyn_idx_args.Push(BuildDynamicIndex(dyn_idx, /* cast_to_u32 */ true));
-                    }
-                    // Construct the dynamic index array, and push as an argument.
-                    new_args.Push(b.Call(dyn_idx_arr_ty, std::move(dyn_idx_args)));
-                }
-            }
-
-            // Make the call to the target's variant.
-            return b.Call(*target_variant, std::move(new_args));
-        });
-    }
-
-    /// ProcessAccessChains performs the following:
-    /// * Removes all AccessChains from expressions that are not either used as a pointer argument
-    ///   in a call, or originates from a pointer parameter.
-    /// * Hoists the dynamic index expressions of AccessChains to 'let' statements, to prevent
-    ///   multiple evaluation of the expressions, and avoid expressions resolving to different
-    ///   variables based on lexical scope.
-    void ProcessAccessChains() {
-        auto chain_exprs = access_chains.Keys();
-        chain_exprs.Sort([](const auto& expr_a, const auto& expr_b) {
-            return expr_a->Declaration()->node_id.value < expr_b->Declaration()->node_id.value;
-        });
-
-        for (auto* expr : chain_exprs) {
-            auto* chain = *access_chains.Get(expr);
-            if (!chain->used_in_call && !chain->root.variable->Is<sem::Parameter>()) {
-                // Chain was not used in a function call, and does not originate from a
-                // parameter. This chain does not need transforming. Drop it.
-                access_chains.Remove(expr);
-                continue;
-            }
-
-            // Chain requires transforming.
-
-            // We need to be careful that the chain does not use expressions with side-effects which
-            // cannot be repeatedly evaluated. In this situation we can hoist the dynamic index
-            // expressions to their own uniquely named lets (if required).
-            MaybeHoistDynamicIndices(chain, expr->Stmt());
-        }
-    }
-
-    /// TransformAccessChainExpressions registers the clone callback to:
-    /// * Transform all expressions that have an AccessChain (which aren't arguments to function
-    ///   calls, these are handled by TransformCall()), into the equivalent expression using a
-    ///   module-scope variable.
-    /// * Replace expressions that have been hoisted to a let, with an identifier expression to that
-    ///   let.
-    void TransformAccessChainExpressions() {
-        // Register a custom handler for all non-function call expressions
-        ctx.ReplaceAll([this](const Expression* ast_expr) -> const Expression* {
-            if (!clone_state->current_variant) {
-                // Expression does not belong to a function variant.
-                return nullptr;  // Just clone the expression.
-            }
-
-            auto* expr = sem.GetVal(ast_expr);
-            if (!expr) {
-                // No semantic node for the expression.
-                return nullptr;  // Just clone the expression.
-            }
-
-            // If the expression has been hoisted to a 'let', then replace the expression with an
-            // identifier to the hoisted let.
-            if (auto hoisted = clone_state->current_function->hoisted_exprs.Get(expr)) {
-                return b.Expr(*hoisted);
-            }
-
-            auto* chain = AccessChainFor(expr);
-            if (!chain) {
-                // The expression does not have an AccessChain.
-                return nullptr;  // Just clone the expression.
-            }
-
-            auto* root_param = chain->root.variable->As<sem::Parameter>();
-            if (!root_param) {
-                // The expression has an access chain, but does not originate with a pointer
-                // parameter. We don't need to change anything here.
-                return nullptr;  // Just clone the expression.
-            }
-
-            auto incoming_shape = clone_state->current_variant_sig->Get(root_param);
-            if (!incoming_shape) {
-                // The root parameter of the access chain is not part of the variant's signature.
-                return nullptr;  // Just clone the expression.
-            }
-
-            // Expression holds an access chain to a pointer parameter that needs transforming.
-            // Reconstruct the expression using the variant's incoming shape.
-
-            auto* chain_expr = BuildAccessRootExpr(incoming_shape->root, /* deref */ true);
-
-            // Chain starts with a pointer parameter.
-            // Replace this with the variant's incoming shape. This will bring the expression up to
-            // the incoming pointer.
-            size_t next_dyn_idx_from_indices = 0;
-            auto& indices =
-                clone_state->current_variant->ptr_param_symbols.Get(root_param)->indices;
-            for (auto param_access : incoming_shape->ops) {
-                chain_expr = BuildAccessExpr(chain_expr, param_access, [&] {
-                    return b.IndexAccessor(indices, AInt(next_dyn_idx_from_indices++));
-                });
-            }
-
-            // Now build the expression chain within the function.
-
-            // For each access in the chain (excluding the pointer parameter)...
-            size_t next_dyn_idx_from_chain = 0;
-            for (auto& op : chain->ops) {
-                chain_expr = BuildAccessExpr(chain_expr, op, [&] {
-                    return BuildDynamicIndex(chain->dynamic_indices[next_dyn_idx_from_chain++],
-                                             false);
-                });
-            }
-
-            // BuildAccessExpr() always returns a non-pointer.
-            // If the expression we're replacing is a pointer, take the address.
-            if (expr->Type()->Is<core::type::Pointer>()) {
-                chain_expr = b.AddressOf(chain_expr);
-            }
-
-            return chain_expr;
-        });
-    }
-
-    /// @returns the FnInfo for the given function, constructing a new FnInfo if @p fn doesn't
-    /// already have one.
-    FnInfo* FnInfoFor(const sem::Function* fn) {
-        return fns.GetOrAdd(fn, [this] { return fn_info_allocator.Create(); });
-    }
-
-    /// @returns the type alias used to hold the dynamic indices for @p shape, declaring a new alias
-    /// if this is the first call for the given shape.
-    Type DynamicIndexArrayType(const AccessShape& shape) {
-        auto name = dynamic_index_array_aliases.GetOrAdd(shape, [&] {
-            // Count the number of dynamic indices
-            uint32_t num_dyn_indices = shape.NumDynamicIndices();
-            if (num_dyn_indices == 0) {
-                return Symbol{};
-            }
-            auto symbol = b.Symbols().New(AccessShapeName(shape));
-            b.Alias(symbol, b.ty.array(b.ty.u32(), u32(num_dyn_indices)));
-            return symbol;
-        });
-        return name.IsValid() ? b.ty(name) : Type{};
-    }
-
-    /// @returns a name describing the given shape
-    std::string AccessShapeName(const AccessShape& shape) {
-        StringStream ss;
-
-        if (IsPrivateOrFunction(shape.root.address_space)) {
-            ss << "F";
-        } else {
-            ss << shape.root.variable->Declaration()->name->symbol.Name();
-        }
-
-        for (auto& op : shape.ops) {
-            ss << "_";
-
-            if (std::holds_alternative<DynamicIndex>(op)) {
-                /// The op uses a dynamic (runtime-expression) index.
-                ss << "X";
-                continue;
-            }
-
-            auto* member = std::get_if<Symbol>(&op);
-            if (DAWN_LIKELY(member)) {
-                ss << member->Name();
-                continue;
-            }
-
-            TINT_ICE() << "unhandled variant for access chain";
-        }
-        return ss.str();
-    }
-
-    /// Builds an expresion to the root of an access, returning the new expression.
-    /// @param root the AccessRoot
-    /// @param deref if true, the returned expression will always be a reference type.
-    const Expression* BuildAccessRootExpr(const AccessRoot& root, bool deref) {
-        if (auto* param = root.variable->As<sem::Parameter>()) {
-            if (auto symbols = clone_state->current_variant->ptr_param_symbols.Get(param)) {
-                if (deref) {
-                    return b.Deref(b.Expr(symbols->base_ptr));
-                }
-                return b.Expr(symbols->base_ptr);
-            }
-        }
-
-        const Expression* expr = b.Expr(ctx.Clone(root.variable->Declaration()->name->symbol));
-        if (deref) {
-            if (root.variable->Type()->Is<core::type::Pointer>()) {
-                expr = b.Deref(expr);
-            }
-        }
-        return expr;
-    }
-
-    /// Builds a single access in an access chain, returning the new expression.
-    /// The returned expression will always be of a reference type.
-    /// @param expr the input expression
-    /// @param access the access to perform on the current expression
-    /// @param dynamic_index a function that obtains the next dynamic index
-    const Expression* BuildAccessExpr(const Expression* expr,
-                                      const AccessOp& access,
-                                      std::function<const Expression*()> dynamic_index) {
-        if (std::holds_alternative<DynamicIndex>(access)) {
-            /// The access uses a dynamic (runtime-expression) index.
-            auto* idx = dynamic_index();
-            return b.IndexAccessor(expr, idx);
-        }
-
-        auto* member = std::get_if<Symbol>(&access);
-        if (DAWN_LIKELY(member)) {
-            /// The access is a member access.
-            return b.MemberAccessor(expr, ctx.Clone(*member));
-        }
-
-        TINT_ICE() << "unhandled variant type for access chain";
-    }
-
-    /// @returns a new Symbol starting with @p symbol concatenated with @p suffix, and possibly an
-    /// underscore and number, if the symbol is already taken.
-    Symbol UniqueSymbolWithSuffix(Symbol symbol, const std::string& suffix) {
-        auto str = symbol.Name() + suffix;
-        return unique_symbols.GetOrAdd(str, [&] { return b.Symbols().New(str); });
-    }
-
-    /// @returns true if the function @p fn has at least one pointer parameter.
-    static bool HasPointerParameter(const sem::Function* fn) {
-        for (auto* param : fn->Parameters()) {
-            if (param->Type()->Is<core::type::Pointer>()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /// @returns true if the function @p fn has at least one pointer parameter in an address space
-    /// that must be replaced. If this function is not called, then the function cannot be sensibly
-    /// generated, and must be stripped.
-    static bool MustBeCalled(const sem::Function* fn) {
-        for (auto* param : fn->Parameters()) {
-            if (auto* ptr = param->Type()->As<core::type::Pointer>()) {
-                switch (ptr->AddressSpace()) {
-                    case core::AddressSpace::kUniform:
-                    case core::AddressSpace::kStorage:
-                    case core::AddressSpace::kWorkgroup:
-                        return true;
-                    default:
-                        return false;
-                }
-            }
-        }
-        return false;
-    }
-
-    /// @returns true if the given address space is 'private' or 'function'.
-    static bool IsPrivateOrFunction(const core::AddressSpace sc) {
-        return sc == core::AddressSpace::kPrivate || sc == core::AddressSpace::kFunction;
-    }
-};
-
-DirectVariableAccess::Config::Config() = default;
-DirectVariableAccess::Config::Config(const Options& opt) : options(opt) {}
-
-DirectVariableAccess::Config::~Config() = default;
-
-DirectVariableAccess::DirectVariableAccess() = default;
-
-DirectVariableAccess::~DirectVariableAccess() = default;
-
-Transform::ApplyResult DirectVariableAccess::Apply(const Program& program,
-                                                   const DataMap& inputs,
-                                                   DataMap&) const {
-    Options options;
-    if (auto* cfg = inputs.Get<Config>()) {
-        options = cfg->options;
-    }
-    return State(program, options).Run();
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access.h b/src/tint/lang/wgsl/ast/transform/direct_variable_access.h
deleted file mode 100644
index 0d2771d..0000000
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access.h
+++ /dev/null
@@ -1,97 +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_WGSL_AST_TRANSFORM_DIRECT_VARIABLE_ACCESS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_DIRECT_VARIABLE_ACCESS_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// DirectVariableAccess is a transform that allows usage of pointer parameters in the 'storage',
-/// 'uniform' and 'workgroup' address space, and passing of pointers to sub-objects. These pointers
-/// are allowed with the `unrestricted_pointer_parameters` WGSL feature.
-///
-/// DirectVariableAccess works by creating specializations of functions that have pointer
-/// parameters, one specialization for each pointer argument's unique access chain 'shape' from a
-/// unique variable. Calls to specialized functions are transformed so that the pointer arguments
-/// are replaced with an array of access-chain indicies, and if the pointer is in the 'function' or
-/// 'private' address space, also with a pointer to the root object. For more information, see the
-/// comments in src/tint/lang/wgsl/ast/transform/direct_variable_access.cc.
-///
-/// @note DirectVariableAccess requires the transform::Unshadow transform to have been run first.
-class DirectVariableAccess final : public Castable<DirectVariableAccess, Transform> {
-  public:
-    /// Constructor
-    DirectVariableAccess();
-    /// Destructor
-    ~DirectVariableAccess() override;
-
-    /// Options adjusts the behaviour of the transform.
-    struct Options {
-        /// If true, then 'private' sub-object pointer arguments will be transformed.
-        bool transform_private = false;
-        /// If true, then 'function' sub-object pointer arguments will be transformed.
-        bool transform_function = false;
-
-        /// Reflection for this struct
-        TINT_REFLECT(Options, transform_private, transform_function);
-    };
-
-    /// Config is consumed by the DirectVariableAccess transform.
-    /// Config specifies the behavior of the transform.
-    struct Config final : public Castable<Config, Data> {
-        /// Constructor
-        Config();
-
-        /// Constructor
-        /// @param options behavior of the transform
-        explicit Config(const Options& options);
-
-        /// Destructor
-        ~Config() override;
-
-        /// The transform behavior options
-        Options options;
-
-        /// Reflection for this struct
-        TINT_REFLECT(Config, options);
-    };
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_DIRECT_VARIABLE_ACCESS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc b/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc
deleted file mode 100644
index 9b18a40..0000000
--- a/src/tint/lang/wgsl/ast/transform/direct_variable_access_test.cc
+++ /dev/null
@@ -1,3576 +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/wgsl/ast/transform/direct_variable_access.h"
-
-#include <memory>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/utils/text/string.h"
-
-namespace tint::ast::transform {
-namespace {
-
-/// @returns a DataMap with DirectVariableAccess::Config::transform_private enabled.
-static DataMap EnablePrivate() {
-    DirectVariableAccess::Options opts;
-    opts.transform_private = true;
-
-    DataMap inputs;
-    inputs.Add<DirectVariableAccess::Config>(opts);
-    return inputs;
-}
-
-/// @returns a DataMap with DirectVariableAccess::Config::transform_function enabled.
-static DataMap EnableFunction() {
-    DirectVariableAccess::Options opts;
-    opts.transform_function = true;
-
-    DataMap inputs;
-    inputs.Add<DirectVariableAccess::Config>(opts);
-    return inputs;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ShouldRun tests
-////////////////////////////////////////////////////////////////////////////////
-namespace should_run {
-
-using DirectVariableAccessShouldRunTest = TransformTest;
-
-TEST_F(DirectVariableAccessShouldRunTest, EmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<DirectVariableAccess>(src));
-}
-
-}  // namespace should_run
-
-////////////////////////////////////////////////////////////////////////////////
-// remove uncalled
-////////////////////////////////////////////////////////////////////////////////
-namespace remove_uncalled {
-
-using DirectVariableAccessRemoveUncalledTest = TransformTest;
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrUniform) {
-    auto* src = R"(
-var<private> keep_me = 42;
-
-fn u(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-)";
-
-    auto* expect = R"(
-var<private> keep_me = 42;
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrStorage) {
-    auto* src = R"(
-var<private> keep_me = 42;
-
-fn s(pre : i32, p : ptr<storage, i32>, post : i32) -> i32 {
-  return *(p);
-}
-)";
-
-    auto* expect = R"(
-var<private> keep_me = 42;
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrWorkgroup) {
-    auto* src = R"(
-var<private> keep_me = 42;
-
-fn w(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-)";
-
-    auto* expect = R"(
-var<private> keep_me = 42;
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrPrivate_Disabled) {
-    auto* src = R"(
-var<private> keep_me = 42;
-
-fn f(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return *(p);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrPrivate_Enabled) {
-    auto* src = R"(
-var<private> keep_me = 42;
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrFunction_Disabled) {
-    auto* src = R"(
-var<private> keep_me = 42;
-
-fn f(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return *(p);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessRemoveUncalledTest, PtrFunction_Enabled) {
-    auto* src = R"(
-var<private> keep_me = 42;
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace remove_uncalled
-
-////////////////////////////////////////////////////////////////////////////////
-// pointer chains
-////////////////////////////////////////////////////////////////////////////////
-namespace pointer_chains_tests {
-
-using DirectVariableAccessPtrChainsTest = TransformTest;
-
-TEST_F(DirectVariableAccessPtrChainsTest, ConstantIndices) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  let p0 = &U;
-  let p1 = &(*p0)[1];
-  let p2 = &(*p1)[1+1];
-  let p3 = &(*p2)[2*2 - 1];
-  a(10, p3, 20);
-}
-
-fn c(p : ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>) {
-  let p0 = p;
-  let p1 = &(*p0)[1];
-  let p2 = &(*p1)[1+1];
-  let p3 = &(*p2)[2*2 - 1];
-  a(10, p3, 20);
-}
-
-fn d() {
-  c(&U);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-alias U_X_X_X = array<u32, 3u>;
-
-fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4<i32> {
-  return U[p[0]][p[1]][p[2]];
-}
-
-fn b() {
-  let p0 = &(U);
-  let p1 = &((*(p0))[1]);
-  let p2 = &((*(p1))[(1 + 1)]);
-  let p3 = &((*(p2))[((2 * 2) - 1)]);
-  a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20);
-}
-
-fn c_U() {
-  let p0 = &(U);
-  let p1 = &(U[1]);
-  let p2 = &(U[1][2]);
-  let p3 = &(U[1][2][3]);
-  a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20);
-}
-
-fn d() {
-  c_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, ConstantIndices_ViaPointerIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  let p0 = &U;
-  let p1 = &p0[1];
-  let p2 = &p1[1+1];
-  let p3 = &p2[2*2 - 1];
-  a(10, p3, 20);
-}
-
-fn c(p : ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>) {
-  let p0 = p;
-  let p1 = &p0[1];
-  let p2 = &p1[1+1];
-  let p3 = &p2[2*2 - 1];
-  a(10, p3, 20);
-}
-
-fn d() {
-  c(&U);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-alias U_X_X_X = array<u32, 3u>;
-
-fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4<i32> {
-  return U[p[0]][p[1]][p[2]];
-}
-
-fn b() {
-  let p0 = &(U);
-  let p1 = &(p0[1]);
-  let p2 = &(p1[(1 + 1)]);
-  let p3 = &(p2[((2 * 2) - 1)]);
-  a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20);
-}
-
-fn c_U() {
-  let p0 = &(U);
-  let p1 = &(U[1]);
-  let p2 = &(U[1][2]);
-  let p3 = &(U[1][2][3]);
-  a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20);
-}
-
-fn d() {
-  c_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistIndices) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
-  i++;
-  return i;
-}
-fn second() -> i32 {
-  i++;
-  return i;
-}
-fn third() -> i32 {
-  i++;
-  return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  let p0 = &U;
-  let p1 = &(*p0)[first()];
-  let p2 = &(*p1)[second()][third()];
-  a(10, p2, 20);
-}
-
-fn c(p : ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>) {
-  let p0 = &U;
-  let p1 = &(*p0)[first()];
-  let p2 = &(*p1)[second()][third()];
-  a(10, p2, 20);
-}
-
-fn d() {
-  c(&U);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
-  i++;
-  return i;
-}
-
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-fn third() -> i32 {
-  i++;
-  return i;
-}
-
-alias U_X_X_X = array<u32, 3u>;
-
-fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4<i32> {
-  return U[p[0]][p[1]][p[2]];
-}
-
-fn b() {
-  let p0 = &(U);
-  let ptr_index_save = first();
-  let p1 = &((*(p0))[ptr_index_save]);
-  let ptr_index_save_1 = second();
-  let ptr_index_save_2 = third();
-  let p2 = &((*(p1))[ptr_index_save_1][ptr_index_save_2]);
-  a_U_X_X_X(10, U_X_X_X(u32(ptr_index_save), u32(ptr_index_save_1), u32(ptr_index_save_2)), 20);
-}
-
-fn c_U() {
-  let p0 = &(U);
-  let ptr_index_save_3 = first();
-  let p1 = &((*(p0))[ptr_index_save_3]);
-  let ptr_index_save_4 = second();
-  let ptr_index_save_5 = third();
-  let p2 = &((*(p1))[ptr_index_save_4][ptr_index_save_5]);
-  a_U_X_X_X(10, U_X_X_X(u32(ptr_index_save_3), u32(ptr_index_save_4), u32(ptr_index_save_5)), 20);
-}
-
-fn d() {
-  c_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopInit) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
-  i++;
-  return i;
-}
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  for (let p1 = &U[first()]; true; ) {
-    a(10, &(*p1)[second()], 20);
-  }
-}
-
-fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
-  for (let p1 = &(*p)[first()]; true; ) {
-    a(10, &(*p1)[second()], 20);
-  }
-}
-
-fn d() {
-  c(&U);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
-  i++;
-  return i;
-}
-
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-alias U_X_X = array<u32, 2u>;
-
-fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
-  return U[p[0]][p[1]];
-}
-
-fn b() {
-  {
-    let ptr_index_save = first();
-    let p1 = &(U[ptr_index_save]);
-    loop {
-      if (!(true)) {
-        break;
-      }
-      {
-        a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(second())), 20);
-      }
-    }
-  }
-}
-
-fn c_U() {
-  {
-    let ptr_index_save_1 = first();
-    let p1 = &(U[ptr_index_save_1]);
-    loop {
-      if (!(true)) {
-        break;
-      }
-      {
-        a_U_X_X(10, U_X_X(u32(ptr_index_save_1), u32(second())), 20);
-      }
-    }
-  }
-}
-
-fn d() {
-  c_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopCond) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
-  i++;
-  return i;
-}
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  let p = &U[first()][second()];
-  for (; a(10, p, 20).x < 4; ) {
-    let body = 1;
-  }
-}
-
-fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
-  let p2 = &(*p)[first()][second()];
-  for (; a(10, p2, 20).x < 4; ) {
-    let body = 1;
-  }
-}
-
-fn d() {
-  c(&U);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
-  i++;
-  return i;
-}
-
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-alias U_X_X = array<u32, 2u>;
-
-fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
-  return U[p[0]][p[1]];
-}
-
-fn b() {
-  let ptr_index_save = first();
-  let ptr_index_save_1 = second();
-  let p = &(U[ptr_index_save][ptr_index_save_1]);
-  for(; (a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20).x < 4); ) {
-    let body = 1;
-  }
-}
-
-fn c_U() {
-  let ptr_index_save_2 = first();
-  let ptr_index_save_3 = second();
-  let p2 = &(U[ptr_index_save_2][ptr_index_save_3]);
-  for(; (a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20).x < 4); ) {
-    let body = 1;
-  }
-}
-
-fn d() {
-  c_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopCont) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
-  i++;
-  return i;
-}
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  let p = &U[first()][second()];
-  for (var i = 0; i < 3; a(10, p, 20)) {
-    i++;
-  }
-}
-
-fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
-  let p2 = &(*p)[first()][second()];
-  for (var i = 0; i < 3; a(10, p2, 20)) {
-    i++;
-  }
-}
-
-fn d() {
-  c(&U);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
-  i++;
-  return i;
-}
-
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-alias U_X_X = array<u32, 2u>;
-
-fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
-  return U[p[0]][p[1]];
-}
-
-fn b() {
-  let ptr_index_save = first();
-  let ptr_index_save_1 = second();
-  let p = &(U[ptr_index_save][ptr_index_save_1]);
-  for(var i = 0; (i < 3); a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20)) {
-    i++;
-  }
-}
-
-fn c_U() {
-  let ptr_index_save_2 = first();
-  let ptr_index_save_3 = second();
-  let p2 = &(U[ptr_index_save_2][ptr_index_save_3]);
-  for(var i = 0; (i < 3); a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20)) {
-    i++;
-  }
-}
-
-fn d() {
-  c_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPtrChainsTest, HoistInWhileCond) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-fn first() -> i32 {
-  i++;
-  return i;
-}
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  let p = &U[first()][second()];
-  while (a(10, p, 20).x < 4) {
-    let body = 1;
-  }
-}
-
-fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
-  let p2 = &(*p)[first()][second()];
-  while (a(10, p2, 20).x < 4) {
-    let body = 1;
-  }
-}
-
-fn d() {
-  c(&U);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
-
-var<private> i : i32;
-
-fn first() -> i32 {
-  i++;
-  return i;
-}
-
-fn second() -> i32 {
-  i++;
-  return i;
-}
-
-alias U_X_X = array<u32, 2u>;
-
-fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
-  return U[p[0]][p[1]];
-}
-
-fn b() {
-  let ptr_index_save = first();
-  let ptr_index_save_1 = second();
-  let p = &(U[ptr_index_save][ptr_index_save_1]);
-  while((a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20).x < 4)) {
-    let body = 1;
-  }
-}
-
-fn c_U() {
-  let ptr_index_save_2 = first();
-  let ptr_index_save_3 = second();
-  let p2 = &(U[ptr_index_save_2][ptr_index_save_3]);
-  while((a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20).x < 4)) {
-    let body = 1;
-  }
-}
-
-fn d() {
-  c_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace pointer_chains_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'uniform' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace uniform_as_tests {
-
-using DirectVariableAccessUniformASTest = TransformTest;
-
-TEST_F(DirectVariableAccessUniformASTest, Param_ptr_i32_read) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : i32;
-
-fn a(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
-  return *p;
-}
-
-fn b() {
-  a(10, &U, 20);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : i32;
-
-fn a_U(pre : i32, post : i32) -> i32 {
-  return U;
-}
-
-fn b() {
-  a_U(10, 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, Param_ptr_vec4i32_Via_array_DynamicRead) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  let I = 3;
-  a(10, &U[I], 20);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-
-alias U_X = array<u32, 1u>;
-
-fn a_U_X(pre : i32, p : U_X, post : i32) -> vec4<i32> {
-  return U[p[0]];
-}
-
-fn b() {
-  let I = 3;
-  a_U_X(10, U_X(u32(I)), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, Param_ptr_vec4i32_Via_array_DynamicRead_ViaPointerDot) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  var I = vec2<i32>(3, 3);
-  let p = &I;
-  a(10, &U[p.x], 20);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-
-alias U_X = array<u32, 1u>;
-
-fn a_U_X(pre : i32, p : U_X, post : i32) -> vec4<i32> {
-  return U[p[0]];
-}
-
-fn b() {
-  var I = vec2<i32>(3, 3);
-  let p = &(I);
-  a_U_X(10, U_X(u32(p.x)), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, CallChaining) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-};
-
-@group(0) @binding(0) var<uniform> U : Outer;
-
-fn f0(p : ptr<uniform, vec4<f32>>) -> f32 {
-  return (*p).x;
-}
-
-fn f1(p : ptr<uniform, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    // call f0() with inline usage of p
-    res += f0(&(*p)[1]);
-  }
-  {
-    // call f0() with pointer-let usage of p
-    let p_vec = &(*p)[1];
-    res += f0(p_vec);
-  }
-  {
-    // call f0() with inline usage of U
-    res += f0(&U.arr[2].mat[1]);
-  }
-  {
-    // call f0() with pointer-let usage of U
-    let p_vec = &U.arr[2].mat[1];
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<uniform, Inner>) -> f32 {
-  let p_mat = &(*p).mat;
-  return f1(p_mat);
-}
-
-fn f3(p0 : ptr<uniform, InnerArr>, p1 : ptr<uniform, mat3x4<f32>>) -> f32 {
-  let p0_inner = &(*p0)[3];
-  return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<uniform, Outer>) -> f32 {
-  return f3(&(*p).arr, &U.mat);
-}
-
-fn b() {
-  f4(&U);
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-@group(0) @binding(0) var<uniform> U : Outer;
-
-alias U_mat_X = array<u32, 1u>;
-
-fn f0_U_mat_X(p : U_mat_X) -> f32 {
-  return U.mat[p[0]].x;
-}
-
-alias U_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_U_arr_X_mat_X(p : U_arr_X_mat_X) -> f32 {
-  return U.arr[p[0]].mat[p[1]].x;
-}
-
-fn f1_U_mat() -> f32 {
-  var res : f32;
-  {
-    res += f0_U_mat_X(U_mat_X(1));
-  }
-  {
-    let p_vec = &(U.mat[1]);
-    res += f0_U_mat_X(U_mat_X(1));
-  }
-  {
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(U.arr[2].mat[1]);
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias U_arr_X_mat = array<u32, 1u>;
-
-fn f1_U_arr_X_mat(p : U_arr_X_mat) -> f32 {
-  var res : f32;
-  {
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    let p_vec = &(U.arr[p[0]].mat[1]);
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(U.arr[2].mat[1]);
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias U_arr_X = array<u32, 1u>;
-
-fn f2_U_arr_X(p : U_arr_X) -> f32 {
-  let p_mat = &(U.arr[p[0]].mat);
-  return f1_U_arr_X_mat(U_arr_X_mat(p[0u]));
-}
-
-fn f3_U_arr_U_mat() -> f32 {
-  let p0_inner = &(U.arr[3]);
-  return (f2_U_arr_X(U_arr_X(3)) + f1_U_mat());
-}
-
-fn f4_U() -> f32 {
-  return f3_U_arr_U_mat();
-}
-
-fn b() {
-  f4_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, CallChaining_ViaPointerDotOrIndex) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-};
-
-@group(0) @binding(0) var<uniform> U : Outer;
-
-fn f0(p : ptr<uniform, vec4<f32>>) -> f32 {
-  return p.x;
-}
-
-fn f1(p : ptr<uniform, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    // call f0() with inline usage of p
-    res += f0(&p[1]);
-  }
-  {
-    // call f0() with pointer-let usage of p
-    let p_vec = &p[1];
-    res += f0(p_vec);
-  }
-  {
-    // call f0() with inline usage of U
-    res += f0(&U.arr[2].mat[1]);
-  }
-  {
-    // call f0() with pointer-let usage of U
-    let p_vec = &U.arr[2].mat[1];
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<uniform, Inner>) -> f32 {
-  let p_mat = &p.mat;
-  return f1(p_mat);
-}
-
-fn f3(p0 : ptr<uniform, InnerArr>, p1 : ptr<uniform, mat3x4<f32>>) -> f32 {
-  let p0_inner = &(*p0)[3];
-  return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<uniform, Outer>) -> f32 {
-  return f3(&p.arr, &U.mat);
-}
-
-fn b() {
-  f4(&U);
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-@group(0) @binding(0) var<uniform> U : Outer;
-
-alias U_mat_X = array<u32, 1u>;
-
-fn f0_U_mat_X(p : U_mat_X) -> f32 {
-  return (&(U.mat[p[0]])).x;
-}
-
-alias U_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_U_arr_X_mat_X(p : U_arr_X_mat_X) -> f32 {
-  return (&(U.arr[p[0]].mat[p[1]])).x;
-}
-
-fn f1_U_mat() -> f32 {
-  var res : f32;
-  {
-    res += f0_U_mat_X(U_mat_X(1));
-  }
-  {
-    let p_vec = &(U.mat[1]);
-    res += f0_U_mat_X(U_mat_X(1));
-  }
-  {
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(U.arr[2].mat[1]);
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias U_arr_X_mat = array<u32, 1u>;
-
-fn f1_U_arr_X_mat(p : U_arr_X_mat) -> f32 {
-  var res : f32;
-  {
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    let p_vec = &(U.arr[p[0]].mat[1]);
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(U.arr[2].mat[1]);
-    res += f0_U_arr_X_mat_X(U_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias U_arr_X = array<u32, 1u>;
-
-fn f2_U_arr_X(p : U_arr_X) -> f32 {
-  let p_mat = &(U.arr[p[0]].mat);
-  return f1_U_arr_X_mat(U_arr_X_mat(p[0u]));
-}
-
-fn f3_U_arr_U_mat() -> f32 {
-  let p0_inner = &(U.arr[3]);
-  return (f2_U_arr_X(U_arr_X(3)) + f1_U_mat());
-}
-
-fn f4_U() -> f32 {
-  return f3_U_arr_U_mat();
-}
-
-fn b() {
-  f4_U();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessUniformASTest, CallChaining2) {
-    auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<uniform> input : T;
-
-fn f2(p : ptr<uniform, T2>) -> T3 {
-  return (*p)[3];
-}
-
-fn f1(p : ptr<uniform, T1>) -> T3 {
-  return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<uniform, T>) -> T3 {
-  return f1(&(*p)[1]);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  f0(&input);
-}
-)";
-
-    auto* expect =
-        R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<uniform> input : T;
-
-alias input_X_X = array<u32, 2u>;
-
-fn f2_input_X_X(p : input_X_X) -> T3 {
-  return input[p[0]][p[1]][3];
-}
-
-alias input_X = array<u32, 1u>;
-
-fn f1_input_X(p : input_X) -> T3 {
-  return f2_input_X_X(input_X_X(p[0u], 2));
-}
-
-fn f0_input() -> T3 {
-  return f1_input_X(input_X(1));
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  f0_input();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace uniform_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'storage' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace storage_as_tests {
-
-using DirectVariableAccessStorageASTest = TransformTest;
-
-TEST_F(DirectVariableAccessStorageASTest, Param_ptr_i32_Via_struct_read) {
-    auto* src = R"(
-struct str {
-  i : i32,
-};
-
-@group(0) @binding(0) var<storage> S : str;
-
-fn a(pre : i32, p : ptr<storage, i32>, post : i32) -> i32 {
-  return *p;
-}
-
-fn b() {
-  a(10, &S.i, 20);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  i : i32,
-}
-
-@group(0) @binding(0) var<storage> S : str;
-
-fn a_S_i(pre : i32, post : i32) -> i32 {
-  return S.i;
-}
-
-fn b() {
-  a_S_i(10, 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, Param_ptr_arr_i32_Via_struct_write) {
-    auto* src = R"(
-struct str {
-  arr : array<i32, 4>,
-};
-
-@group(0) @binding(0) var<storage, read_write> S : str;
-
-fn a(pre : i32, p : ptr<storage, array<i32, 4>, read_write>, post : i32) {
-  *p = array<i32, 4>();
-}
-
-fn b() {
-  a(10, &S.arr, 20);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  arr : array<i32, 4>,
-}
-
-@group(0) @binding(0) var<storage, read_write> S : str;
-
-fn a_S_arr(pre : i32, post : i32) {
-  S.arr = array<i32, 4>();
-}
-
-fn b() {
-  a_S_arr(10, 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, Param_ptr_vec4i32_Via_array_DynamicWrite) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<storage, vec4<i32>, read_write>, post : i32) {
-  *p = vec4<i32>();
-}
-
-fn b() {
-  let I = 3;
-  a(10, &S[I], 20);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
-
-alias S_X = array<u32, 1u>;
-
-fn a_S_X(pre : i32, p : S_X, post : i32) {
-  S[p[0]] = vec4<i32>();
-}
-
-fn b() {
-  let I = 3;
-  a_S_X(10, S_X(u32(I)), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, CallChaining) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-};
-
-@group(0) @binding(0) var<storage> S : Outer;
-
-fn f0(p : ptr<storage, vec4<f32>>) -> f32 {
-  return (*p).x;
-}
-
-fn f1(p : ptr<storage, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    // call f0() with inline usage of p
-    res += f0(&(*p)[1]);
-  }
-  {
-    // call f0() with pointer-let usage of p
-    let p_vec = &(*p)[1];
-    res += f0(p_vec);
-  }
-  {
-    // call f0() with inline usage of S
-    res += f0(&S.arr[2].mat[1]);
-  }
-  {
-    // call f0() with pointer-let usage of S
-    let p_vec = &S.arr[2].mat[1];
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<storage, Inner>) -> f32 {
-  let p_mat = &(*p).mat;
-  return f1(p_mat);
-}
-
-fn f3(p0 : ptr<storage, InnerArr>, p1 : ptr<storage, mat3x4<f32>>) -> f32 {
-  let p0_inner = &(*p0)[3];
-  return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<storage, Outer>) -> f32 {
-  return f3(&(*p).arr, &S.mat);
-}
-
-fn b() {
-  f4(&S);
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-@group(0) @binding(0) var<storage> S : Outer;
-
-alias S_mat_X = array<u32, 1u>;
-
-fn f0_S_mat_X(p : S_mat_X) -> f32 {
-  return S.mat[p[0]].x;
-}
-
-alias S_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_S_arr_X_mat_X(p : S_arr_X_mat_X) -> f32 {
-  return S.arr[p[0]].mat[p[1]].x;
-}
-
-fn f1_S_mat() -> f32 {
-  var res : f32;
-  {
-    res += f0_S_mat_X(S_mat_X(1));
-  }
-  {
-    let p_vec = &(S.mat[1]);
-    res += f0_S_mat_X(S_mat_X(1));
-  }
-  {
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(S.arr[2].mat[1]);
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias S_arr_X_mat = array<u32, 1u>;
-
-fn f1_S_arr_X_mat(p : S_arr_X_mat) -> f32 {
-  var res : f32;
-  {
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    let p_vec = &(S.arr[p[0]].mat[1]);
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(S.arr[2].mat[1]);
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias S_arr_X = array<u32, 1u>;
-
-fn f2_S_arr_X(p : S_arr_X) -> f32 {
-  let p_mat = &(S.arr[p[0]].mat);
-  return f1_S_arr_X_mat(S_arr_X_mat(p[0u]));
-}
-
-fn f3_S_arr_S_mat() -> f32 {
-  let p0_inner = &(S.arr[3]);
-  return (f2_S_arr_X(S_arr_X(3)) + f1_S_mat());
-}
-
-fn f4_S() -> f32 {
-  return f3_S_arr_S_mat();
-}
-
-fn b() {
-  f4_S();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, CallChaining_ViaPointerDotOrIndex) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-};
-
-@group(0) @binding(0) var<storage> S : Outer;
-
-fn f0(p : ptr<storage, vec4<f32>>) -> f32 {
-  return p.x;
-}
-
-fn f1(p : ptr<storage, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    // call f0() with inline usage of p
-    res += f0(&p[1]);
-  }
-  {
-    // call f0() with pointer-let usage of p
-    let p_vec = &p[1];
-    res += f0(p_vec);
-  }
-  {
-    // call f0() with inline usage of S
-    res += f0(&S.arr[2].mat[1]);
-  }
-  {
-    // call f0() with pointer-let usage of S
-    let p_vec = &S.arr[2].mat[1];
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<storage, Inner>) -> f32 {
-  let p_mat = &p.mat;
-  return f1(p_mat);
-}
-
-fn f3(p0 : ptr<storage, InnerArr>, p1 : ptr<storage, mat3x4<f32>>) -> f32 {
-  let p0_inner = &p0[3];
-  return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<storage, Outer>) -> f32 {
-  return f3(&p.arr, &S.mat);
-}
-
-fn b() {
-  f4(&S);
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-@group(0) @binding(0) var<storage> S : Outer;
-
-alias S_mat_X = array<u32, 1u>;
-
-fn f0_S_mat_X(p : S_mat_X) -> f32 {
-  return (&(S.mat[p[0]])).x;
-}
-
-alias S_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_S_arr_X_mat_X(p : S_arr_X_mat_X) -> f32 {
-  return (&(S.arr[p[0]].mat[p[1]])).x;
-}
-
-fn f1_S_mat() -> f32 {
-  var res : f32;
-  {
-    res += f0_S_mat_X(S_mat_X(1));
-  }
-  {
-    let p_vec = &(S.mat[1]);
-    res += f0_S_mat_X(S_mat_X(1));
-  }
-  {
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(S.arr[2].mat[1]);
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias S_arr_X_mat = array<u32, 1u>;
-
-fn f1_S_arr_X_mat(p : S_arr_X_mat) -> f32 {
-  var res : f32;
-  {
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    let p_vec = &(S.arr[p[0]].mat[1]);
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(S.arr[2].mat[1]);
-    res += f0_S_arr_X_mat_X(S_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias S_arr_X = array<u32, 1u>;
-
-fn f2_S_arr_X(p : S_arr_X) -> f32 {
-  let p_mat = &(S.arr[p[0]].mat);
-  return f1_S_arr_X_mat(S_arr_X_mat(p[0u]));
-}
-
-fn f3_S_arr_S_mat() -> f32 {
-  let p0_inner = &(S.arr[3]);
-  return (f2_S_arr_X(S_arr_X(3)) + f1_S_mat());
-}
-
-fn f4_S() -> f32 {
-  return f3_S_arr_S_mat();
-}
-
-fn b() {
-  f4_S();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessStorageASTest, CallChaining2) {
-    auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<storage> input : T;
-
-fn f2(p : ptr<storage, T2>) -> T3 {
-  return (*p)[3];
-}
-
-fn f1(p : ptr<storage, T1>) -> T3 {
-  return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<storage, T>) -> T3 {
-  return f1(&(*p)[1]);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  f0(&input);
-}
-)";
-
-    auto* expect =
-        R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<storage> input : T;
-
-alias input_X_X = array<u32, 2u>;
-
-fn f2_input_X_X(p : input_X_X) -> T3 {
-  return input[p[0]][p[1]][3];
-}
-
-alias input_X = array<u32, 1u>;
-
-fn f1_input_X(p : input_X) -> T3 {
-  return f2_input_X_X(input_X_X(p[0u], 2));
-}
-
-fn f0_input() -> T3 {
-  return f1_input_X(input_X(1));
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  f0_input();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace storage_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'workgroup' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace workgroup_as_tests {
-
-using DirectVariableAccessWorkgroupASTest = TransformTest;
-
-TEST_F(DirectVariableAccessWorkgroupASTest, Param_ptr_vec4i32_Via_array_StaticRead) {
-    auto* src = R"(
-var<workgroup> W : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  a(10, &W[3], 20);
-}
-)";
-
-    auto* expect = R"(
-var<workgroup> W : array<vec4<i32>, 8>;
-
-alias W_X = array<u32, 1u>;
-
-fn a_W_X(pre : i32, p : W_X, post : i32) -> vec4<i32> {
-  return W[p[0]];
-}
-
-fn b() {
-  a_W_X(10, W_X(3), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessWorkgroupASTest, Param_ptr_vec4i32_Via_array_StaticWrite) {
-    auto* src = R"(
-var<workgroup> W : array<vec4<i32>, 8>;
-
-fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) {
-  *p = vec4<i32>();
-}
-
-fn b() {
-  a(10, &W[3], 20);
-}
-)";
-
-    auto* expect = R"(
-var<workgroup> W : array<vec4<i32>, 8>;
-
-alias W_X = array<u32, 1u>;
-
-fn a_W_X(pre : i32, p : W_X, post : i32) {
-  W[p[0]] = vec4<i32>();
-}
-
-fn b() {
-  a_W_X(10, W_X(3), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-};
-
-var<workgroup> W : Outer;
-
-fn f0(p : ptr<workgroup, vec4<f32>>) -> f32 {
-  return (*p).x;
-}
-
-fn f1(p : ptr<workgroup, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    // call f0() with inline usage of p
-    res += f0(&(*p)[1]);
-  }
-  {
-    // call f0() with pointer-let usage of p
-    let p_vec = &(*p)[1];
-    res += f0(p_vec);
-  }
-  {
-    // call f0() with inline usage of W
-    res += f0(&W.arr[2].mat[1]);
-  }
-  {
-    // call f0() with pointer-let usage of W
-    let p_vec = &W.arr[2].mat[1];
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<workgroup, Inner>) -> f32 {
-  let p_mat = &(*p).mat;
-  return f1(p_mat);
-}
-
-fn f3(p0 : ptr<workgroup, InnerArr>, p1 : ptr<workgroup, mat3x4<f32>>) -> f32 {
-  let p0_inner = &(*p0)[3];
-  return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<workgroup, Outer>) -> f32 {
-  return f3(&(*p).arr, &W.mat);
-}
-
-fn b() {
-  f4(&W);
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-var<workgroup> W : Outer;
-
-alias W_mat_X = array<u32, 1u>;
-
-fn f0_W_mat_X(p : W_mat_X) -> f32 {
-  return W.mat[p[0]].x;
-}
-
-alias W_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_W_arr_X_mat_X(p : W_arr_X_mat_X) -> f32 {
-  return W.arr[p[0]].mat[p[1]].x;
-}
-
-fn f1_W_mat() -> f32 {
-  var res : f32;
-  {
-    res += f0_W_mat_X(W_mat_X(1));
-  }
-  {
-    let p_vec = &(W.mat[1]);
-    res += f0_W_mat_X(W_mat_X(1));
-  }
-  {
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(W.arr[2].mat[1]);
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias W_arr_X_mat = array<u32, 1u>;
-
-fn f1_W_arr_X_mat(p : W_arr_X_mat) -> f32 {
-  var res : f32;
-  {
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    let p_vec = &(W.arr[p[0]].mat[1]);
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(W.arr[2].mat[1]);
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias W_arr_X = array<u32, 1u>;
-
-fn f2_W_arr_X(p : W_arr_X) -> f32 {
-  let p_mat = &(W.arr[p[0]].mat);
-  return f1_W_arr_X_mat(W_arr_X_mat(p[0u]));
-}
-
-fn f3_W_arr_W_mat() -> f32 {
-  let p0_inner = &(W.arr[3]);
-  return (f2_W_arr_X(W_arr_X(3)) + f1_W_mat());
-}
-
-fn f4_W() -> f32 {
-  return f3_W_arr_W_mat();
-}
-
-fn b() {
-  f4_W();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining_ViaPointerDotOrIndex) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-};
-
-var<workgroup> W : Outer;
-
-fn f0(p : ptr<workgroup, vec4<f32>>) -> f32 {
-  return p.x;
-}
-
-fn f1(p : ptr<workgroup, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    // call f0() with inline usage of p
-    res += f0(&p[1]);
-  }
-  {
-    // call f0() with pointer-let usage of p
-    let p_vec = &p[1];
-    res += f0(p_vec);
-  }
-  {
-    // call f0() with inline usage of W
-    res += f0(&W.arr[2].mat[1]);
-  }
-  {
-    // call f0() with pointer-let usage of W
-    let p_vec = &W.arr[2].mat[1];
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<workgroup, Inner>) -> f32 {
-  let p_mat = &p.mat;
-  return f1(p_mat);
-}
-
-fn f3(p0 : ptr<workgroup, InnerArr>, p1 : ptr<workgroup, mat3x4<f32>>) -> f32 {
-  let p0_inner = &p0[3];
-  return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<workgroup, Outer>) -> f32 {
-  return f3(&p.arr, &W.mat);
-}
-
-fn b() {
-  f4(&W);
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-var<workgroup> W : Outer;
-
-alias W_mat_X = array<u32, 1u>;
-
-fn f0_W_mat_X(p : W_mat_X) -> f32 {
-  return (&(W.mat[p[0]])).x;
-}
-
-alias W_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_W_arr_X_mat_X(p : W_arr_X_mat_X) -> f32 {
-  return (&(W.arr[p[0]].mat[p[1]])).x;
-}
-
-fn f1_W_mat() -> f32 {
-  var res : f32;
-  {
-    res += f0_W_mat_X(W_mat_X(1));
-  }
-  {
-    let p_vec = &(W.mat[1]);
-    res += f0_W_mat_X(W_mat_X(1));
-  }
-  {
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(W.arr[2].mat[1]);
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias W_arr_X_mat = array<u32, 1u>;
-
-fn f1_W_arr_X_mat(p : W_arr_X_mat) -> f32 {
-  var res : f32;
-  {
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    let p_vec = &(W.arr[p[0]].mat[1]);
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1));
-  }
-  {
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
-  }
-  {
-    let p_vec = &(W.arr[2].mat[1]);
-    res += f0_W_arr_X_mat_X(W_arr_X_mat_X(2, 1));
-  }
-  return res;
-}
-
-alias W_arr_X = array<u32, 1u>;
-
-fn f2_W_arr_X(p : W_arr_X) -> f32 {
-  let p_mat = &(W.arr[p[0]].mat);
-  return f1_W_arr_X_mat(W_arr_X_mat(p[0u]));
-}
-
-fn f3_W_arr_W_mat() -> f32 {
-  let p0_inner = &(W.arr[3]);
-  return (f2_W_arr_X(W_arr_X(3)) + f1_W_mat());
-}
-
-fn f4_W() -> f32 {
-  return f3_W_arr_W_mat();
-}
-
-fn b() {
-  f4_W();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining2) {
-    auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<storage, read> input : T;
-
-fn f2(p : ptr<workgroup, T2>) -> T3 {
-  return (*p)[3];
-}
-
-fn f1(p : ptr<workgroup, T1>) -> T3 {
-  return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<workgroup, T>) -> T3 {
-  return f1(&(*p)[1]);
-}
-
-var<workgroup> W : T;
-@compute @workgroup_size(1)
-fn main() {
-  W = input;
-  f0(&W);
-}
-)";
-
-    auto* expect =
-        R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-@binding(0) @group(0) var<storage, read> input : T;
-
-alias W_X_X = array<u32, 2u>;
-
-fn f2_W_X_X(p : W_X_X) -> T3 {
-  return W[p[0]][p[1]][3];
-}
-
-alias W_X = array<u32, 1u>;
-
-fn f1_W_X(p : W_X) -> T3 {
-  return f2_W_X_X(W_X_X(p[0u], 2));
-}
-
-fn f0_W() -> T3 {
-  return f1_W_X(W_X(1));
-}
-
-var<workgroup> W : T;
-
-@compute @workgroup_size(1)
-fn main() {
-  W = input;
-  f0_W();
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace workgroup_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'private' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace private_as_tests {
-
-using DirectVariableAccessPrivateASTest = TransformTest;
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_read) {
-    auto* src = R"(
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-var<private> P : i32;
-
-fn b() {
-  a(10, &(P), 20);
-}
-)";
-
-    auto* expect = R"(
-fn a_F(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-var<private> P : i32;
-
-fn b() {
-  a_F(10, &(P), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_write) {
-    auto* src = R"(
-fn a(pre : i32, p : ptr<private, i32>, post : i32) {
-  *(p) = 42;
-}
-
-var<private> P : i32;
-
-fn b() {
-  a(10, &(P), 20);
-}
-)";
-
-    auto* expect = R"(
-fn a_F(pre : i32, p : ptr<private, i32>, post : i32) {
-  *(p) = 42;
-}
-
-var<private> P : i32;
-
-fn b() {
-  a_F(10, &(P), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_Via_struct_read) {
-    auto* src = R"(
-struct str {
-  i : i32,
-};
-
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return *p;
-}
-
-var<private> P : str;
-
-fn b() {
-  a(10, &P.i, 20);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  i : i32,
-}
-
-fn a_F_i(pre : i32, p : ptr<private, str>, post : i32) -> i32 {
-  return (*(p)).i;
-}
-
-var<private> P : str;
-
-fn b() {
-  a_F_i(10, &(P), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_i32_Via_struct_read) {
-    auto* src = R"(
-struct str {
-  i : i32,
-}
-
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-var<private> P : str;
-
-fn b() {
-  a(10, &(P.i), 20);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_arr_i32_Via_struct_write) {
-    auto* src = R"(
-struct str {
-  arr : array<i32, 4>,
-};
-
-fn a(pre : i32, p : ptr<private, array<i32, 4>>, post : i32) {
-  *p = array<i32, 4>();
-}
-
-var<private> P : str;
-
-fn b() {
-  a(10, &P.arr, 20);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  arr : array<i32, 4>,
-}
-
-fn a_F_arr(pre : i32, p : ptr<private, str>, post : i32) {
-  (*(p)).arr = array<i32, 4>();
-}
-
-var<private> P : str;
-
-fn b() {
-  a_F_arr(10, &(P), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_arr_i32_Via_struct_write) {
-    auto* src = R"(
-struct str {
-  arr : array<i32, 4>,
-}
-
-fn a(pre : i32, p : ptr<private, array<i32, 4>>, post : i32) {
-  *(p) = array<i32, 4>();
-}
-
-var<private> P : str;
-
-fn b() {
-  a(10, &(P.arr), 20);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_mixed) {
-    auto* src = R"(
-struct str {
-  i : i32,
-};
-
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return *p;
-}
-
-var<private> Pi : i32;
-var<private> Ps : str;
-var<private> Pa : array<i32, 4>;
-
-fn b() {
-  a(10, &Pi, 20);
-  a(30, &Ps.i, 40);
-  a(50, &Pa[2], 60);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  i : i32,
-}
-
-fn a_F(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-fn a_F_i(pre : i32, p : ptr<private, str>, post : i32) -> i32 {
-  return (*(p)).i;
-}
-
-alias F_X = array<u32, 1u>;
-
-fn a_F_X(pre : i32, p_base : ptr<private, array<i32, 4u>>, p_indices : F_X, post : i32) -> i32 {
-  return (*(p_base))[p_indices[0]];
-}
-
-var<private> Pi : i32;
-
-var<private> Ps : str;
-
-var<private> Pa : array<i32, 4>;
-
-alias F_X_1 = array<u32, 1u>;
-
-fn b() {
-  a_F(10, &(Pi), 20);
-  a_F_i(30, &(Ps), 40);
-  a_F_X(50, &(Pa), F_X_1(2), 60);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_i32_mixed) {
-    auto* src = R"(
-struct str {
-  i : i32,
-}
-
-fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-var<private> Pi : i32;
-
-var<private> Ps : str;
-
-var<private> Pa : array<i32, 4>;
-
-fn b() {
-  a(10, &(Pi), 20);
-  a(10, &(Ps.i), 20);
-  a(10, &(Pa[2]), 20);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_CallChaining) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-};
-
-var<private> P : Outer;
-
-fn f0(p : ptr<private, vec4<f32>>) -> f32 {
-  return (*p).x;
-}
-
-fn f1(p : ptr<private, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    // call f0() with inline usage of p
-    res += f0(&(*p)[1]);
-  }
-  {
-    // call f0() with pointer-let usage of p
-    let p_vec = &(*p)[1];
-    res += f0(p_vec);
-  }
-  {
-    // call f0() with inline usage of P
-    res += f0(&P.arr[2].mat[1]);
-  }
-  {
-    // call f0() with pointer-let usage of P
-    let p_vec = &P.arr[2].mat[1];
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<private, Inner>) -> f32 {
-  let p_mat = &(*p).mat;
-  return f1(p_mat);
-}
-
-fn f3(p0 : ptr<private, InnerArr>, p1 : ptr<private, mat3x4<f32>>) -> f32 {
-  let p0_inner = &(*p0)[3];
-  return f2(p0_inner) + f1(p1);
-}
-
-fn f4(p : ptr<private, Outer>) -> f32 {
-  return f3(&(*p).arr, &P.mat);
-}
-
-fn b() {
-  f4(&P);
-}
-)";
-
-    auto* expect = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-var<private> P : Outer;
-
-alias F_mat_X = array<u32, 1u>;
-
-fn f0_F_mat_X(p_base : ptr<private, Outer>, p_indices : F_mat_X) -> f32 {
-  return (*(p_base)).mat[p_indices[0]].x;
-}
-
-alias F_arr_X_mat_X = array<u32, 2u>;
-
-fn f0_F_arr_X_mat_X(p_base : ptr<private, Outer>, p_indices : F_arr_X_mat_X) -> f32 {
-  return (*(p_base)).arr[p_indices[0]].mat[p_indices[1]].x;
-}
-
-alias F_mat_X_1 = array<u32, 1u>;
-
-alias F_arr_X_mat_X_1 = array<u32, 2u>;
-
-fn f1_F_mat(p : ptr<private, Outer>) -> f32 {
-  var res : f32;
-  {
-    res += f0_F_mat_X(p, F_mat_X_1(1));
-  }
-  {
-    let p_vec = &((*(p)).mat[1]);
-    res += f0_F_mat_X(p, F_mat_X_1(1));
-  }
-  {
-    res += f0_F_arr_X_mat_X(&(P), F_arr_X_mat_X_1(2, 1));
-  }
-  {
-    let p_vec = &(P.arr[2].mat[1]);
-    res += f0_F_arr_X_mat_X(&(P), F_arr_X_mat_X_1(2, 1));
-  }
-  return res;
-}
-
-alias F_arr_X_mat = array<u32, 1u>;
-
-alias F_arr_X_mat_X_2 = array<u32, 2u>;
-
-fn f1_F_arr_X_mat(p_base : ptr<private, Outer>, p_indices : F_arr_X_mat) -> f32 {
-  var res : f32;
-  {
-    res += f0_F_arr_X_mat_X(p_base, F_arr_X_mat_X_2(p_indices[0u], 1));
-  }
-  {
-    let p_vec = &((*(p_base)).arr[p_indices[0]].mat[1]);
-    res += f0_F_arr_X_mat_X(p_base, F_arr_X_mat_X_2(p_indices[0u], 1));
-  }
-  {
-    res += f0_F_arr_X_mat_X(&(P), F_arr_X_mat_X_1(2, 1));
-  }
-  {
-    let p_vec = &(P.arr[2].mat[1]);
-    res += f0_F_arr_X_mat_X(&(P), F_arr_X_mat_X_1(2, 1));
-  }
-  return res;
-}
-
-alias F_arr_X = array<u32, 1u>;
-
-alias F_arr_X_mat_1 = array<u32, 1u>;
-
-fn f2_F_arr_X(p_base : ptr<private, Outer>, p_indices : F_arr_X) -> f32 {
-  let p_mat = &((*(p_base)).arr[p_indices[0]].mat);
-  return f1_F_arr_X_mat(p_base, F_arr_X_mat_1(p_indices[0u]));
-}
-
-alias F_arr_X_1 = array<u32, 1u>;
-
-fn f3_F_arr_F_mat(p0 : ptr<private, Outer>, p1 : ptr<private, Outer>) -> f32 {
-  let p0_inner = &((*(p0)).arr[3]);
-  return (f2_F_arr_X(p0, F_arr_X_1(3)) + f1_F_mat(p1));
-}
-
-fn f4_F(p : ptr<private, Outer>) -> f32 {
-  return f3_F_arr_F_mat(p, &(P));
-}
-
-fn b() {
-  f4_F(&(P));
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Enabled_CallChaining2) {
-    auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-fn f2(p : ptr<private, T2>) -> T3 {
-  return (*p)[3];
-}
-
-fn f1(p : ptr<private, T1>) -> T3 {
-  return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<private, T>) -> T3 {
-  return f1(&(*p)[1]);
-}
-
-var<private> P : T;
-
-@compute @workgroup_size(1)
-fn main() {
-  f0(&P);
-}
-)";
-
-    auto* expect =
-        R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-alias F_X_X = array<u32, 2u>;
-
-fn f2_F_X_X(p_base : ptr<private, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>, p_indices : F_X_X) -> T3 {
-  return (*(p_base))[p_indices[0]][p_indices[1]][3];
-}
-
-alias F_X = array<u32, 1u>;
-
-alias F_X_X_1 = array<u32, 2u>;
-
-fn f1_F_X(p_base : ptr<private, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>, p_indices : F_X) -> T3 {
-  return f2_F_X_X(p_base, F_X_X_1(p_indices[0u], 2));
-}
-
-alias F_X_1 = array<u32, 1u>;
-
-fn f0_F(p : ptr<private, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>) -> T3 {
-  return f1_F_X(p, F_X_1(1));
-}
-
-var<private> P : T;
-
-@compute @workgroup_size(1)
-fn main() {
-  f0_F(&(P));
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnablePrivate());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessPrivateASTest, Disabled_CallChaining) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-var<private> P : Outer;
-
-fn f0(p : ptr<private, vec4<f32>>) -> f32 {
-  return (*(p)).x;
-}
-
-fn f1(p : ptr<private, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    res += f0(&((*(p))[1]));
-  }
-  {
-    let p_vec = &((*(p))[1]);
-    res += f0(p_vec);
-  }
-  {
-    res += f0(&(P.arr[2].mat[1]));
-  }
-  {
-    let p_vec = &(P.arr[2].mat[1]);
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<private, Inner>) -> f32 {
-  let p_mat = &((*(p)).mat);
-  return f1(p_mat);
-}
-
-fn f3(p0 : ptr<private, InnerArr>, p1 : ptr<private, mat3x4<f32>>) -> f32 {
-  let p0_inner = &((*(p0))[3]);
-  return (f2(p0_inner) + f1(p1));
-}
-
-fn f4(p : ptr<private, Outer>) -> f32 {
-  return f3(&((*(p)).arr), &(P.mat));
-}
-
-fn b() {
-  f4(&(P));
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace private_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// 'function' address space
-////////////////////////////////////////////////////////////////////////////////
-namespace function_as_tests {
-
-using DirectVariableAccessFunctionASTest = TransformTest;
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_LocalPtr) {
-    auto* src = R"(
-fn f() {
-  var v : i32;
-  let p : ptr<function, i32> = &(v);
-  var x : i32 = *(p);
-}
-)";
-
-    auto* expect = src;  // Nothing changes
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_read) {
-    auto* src = R"(
-fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-fn b() {
-  var F : i32;
-  a(10, &(F), 20);
-}
-)";
-
-    auto* expect = R"(
-fn a_F(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-fn b() {
-  var F : i32;
-  a_F(10, &(F), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_write) {
-    auto* src = R"(
-fn a(pre : i32, p : ptr<function, i32>, post : i32) {
-  *(p) = 42;
-}
-
-fn b() {
-  var F : i32;
-  a(10, &(F), 20);
-}
-)";
-
-    auto* expect = R"(
-fn a_F(pre : i32, p : ptr<function, i32>, post : i32) {
-  *(p) = 42;
-}
-
-fn b() {
-  var F : i32;
-  a_F(10, &(F), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_Via_struct_read) {
-    auto* src = R"(
-struct str {
-  i : i32,
-};
-
-fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return *p;
-}
-
-fn b() {
-  var F : str;
-  a(10, &F.i, 20);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  i : i32,
-}
-
-fn a_F_i(pre : i32, p : ptr<function, str>, post : i32) -> i32 {
-  return (*(p)).i;
-}
-
-fn b() {
-  var F : str;
-  a_F_i(10, &(F), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_arr_i32_Via_struct_write) {
-    auto* src = R"(
-struct str {
-  arr : array<i32, 4>,
-};
-
-fn a(pre : i32, p : ptr<function, array<i32, 4>>, post : i32) {
-  *p = array<i32, 4>();
-}
-
-fn b() {
-  var F : str;
-  a(10, &F.arr, 20);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  arr : array<i32, 4>,
-}
-
-fn a_F_arr(pre : i32, p : ptr<function, str>, post : i32) {
-  (*(p)).arr = array<i32, 4>();
-}
-
-fn b() {
-  var F : str;
-  a_F_arr(10, &(F), 20);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_mixed) {
-    auto* src = R"(
-struct str {
-  i : i32,
-};
-
-fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return *p;
-}
-
-fn b() {
-  var Fi : i32;
-  var Fs : str;
-  var Fa : array<i32, 4>;
-
-  a(10, &Fi, 20);
-  a(30, &Fs.i, 40);
-  a(50, &Fa[2], 60);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  i : i32,
-}
-
-fn a_F(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-fn a_F_i(pre : i32, p : ptr<function, str>, post : i32) -> i32 {
-  return (*(p)).i;
-}
-
-alias F_X = array<u32, 1u>;
-
-fn a_F_X(pre : i32, p_base : ptr<function, array<i32, 4u>>, p_indices : F_X, post : i32) -> i32 {
-  return (*(p_base))[p_indices[0]];
-}
-
-alias F_X_1 = array<u32, 1u>;
-
-fn b() {
-  var Fi : i32;
-  var Fs : str;
-  var Fa : array<i32, 4>;
-  a_F(10, &(Fi), 20);
-  a_F_i(30, &(Fs), 40);
-  a_F_X(50, &(Fa), F_X_1(2), 60);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_CallChaining) {
-    auto* src = R"(
-struct Inner {
-  mat : mat3x4<f32>,
-};
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-};
-
-fn f0(p : ptr<function, vec4<f32>>) -> f32 {
-  return (*p).x;
-}
-
-fn f1(p : ptr<function, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    // call f0() with inline usage of p
-    res += f0(&(*p)[1]);
-  }
-  {
-    // call f0() with pointer-let usage of p
-    let p_vec = &(*p)[1];
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<function, Inner>) -> f32 {
-  let p_mat = &(*p).mat;
-  return f1(p_mat);
-}
-
-fn f3(p : ptr<function, InnerArr>) -> f32 {
-  let p_inner = &(*p)[3];
-  return f2(p_inner);
-}
-
-fn f4(p : ptr<function, Outer>) -> f32 {
-  return f3(&(*p).arr);
-}
-
-fn b() {
-  var S : Outer;
-  f4(&S);
-}
-)";
-
-    auto* expect =
-        R"(
-struct Inner {
-  mat : mat3x4<f32>,
-}
-
-alias InnerArr = array<Inner, 4>;
-
-struct Outer {
-  arr : InnerArr,
-  mat : mat3x4<f32>,
-}
-
-fn f0(p : ptr<function, vec4<f32>>) -> f32 {
-  return (*(p)).x;
-}
-
-fn f1(p : ptr<function, mat3x4<f32>>) -> f32 {
-  var res : f32;
-  {
-    res += f0(&((*(p))[1]));
-  }
-  {
-    let p_vec = &((*(p))[1]);
-    res += f0(p_vec);
-  }
-  return res;
-}
-
-fn f2(p : ptr<function, Inner>) -> f32 {
-  let p_mat = &((*(p)).mat);
-  return f1(p_mat);
-}
-
-fn f3(p : ptr<function, InnerArr>) -> f32 {
-  let p_inner = &((*(p))[3]);
-  return f2(p_inner);
-}
-
-fn f4(p : ptr<function, Outer>) -> f32 {
-  return f3(&((*(p)).arr));
-}
-
-fn b() {
-  var S : Outer;
-  f4(&(S));
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Enabled_CallChaining2) {
-    auto* src = R"(
-alias T3 = vec4i;
-alias T2 = array<T3, 5>;
-alias T1 = array<T2, 5>;
-alias T = array<T1, 5>;
-
-fn f2(p : ptr<function, T2>) -> T3 {
-  return (*p)[3];
-}
-
-fn f1(p : ptr<function, T1>) -> T3 {
-  return f2(&(*p)[2]);
-}
-
-fn f0(p : ptr<function, T>) -> T3 {
-  return f1(&(*p)[1]);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : T;
-  f0(&v);
-}
-)";
-
-    auto* expect =
-        R"(
-alias T3 = vec4i;
-
-alias T2 = array<T3, 5>;
-
-alias T1 = array<T2, 5>;
-
-alias T = array<T1, 5>;
-
-alias F_X_X = array<u32, 2u>;
-
-fn f2_F_X_X(p_base : ptr<function, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>, p_indices : F_X_X) -> T3 {
-  return (*(p_base))[p_indices[0]][p_indices[1]][3];
-}
-
-alias F_X = array<u32, 1u>;
-
-alias F_X_X_1 = array<u32, 2u>;
-
-fn f1_F_X(p_base : ptr<function, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>, p_indices : F_X) -> T3 {
-  return f2_F_X_X(p_base, F_X_X_1(p_indices[0u], 2));
-}
-
-alias F_X_1 = array<u32, 1u>;
-
-fn f0_F(p : ptr<function, array<array<array<vec4<i32>, 5u>, 5u>, 5u>>) -> T3 {
-  return f1_F_X(p, F_X_1(1));
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : T;
-  f0_F(&(v));
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Disabled_Param_ptr_i32_Via_struct_read) {
-    auto* src = R"(
-struct str {
-  i : i32,
-}
-
-fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return *(p);
-}
-
-fn b() {
-  var F : str;
-  a(10, &(F.i), 20);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, Disabled_Param_ptr_arr_i32_Via_struct_write) {
-    auto* src = R"(
-struct str {
-  arr : array<i32, 4>,
-}
-
-fn a(pre : i32, p : ptr<function, array<i32, 4>>, post : i32) {
-  *(p) = array<i32, 4>();
-}
-
-fn b() {
-  var F : str;
-  a(10, &(F.arr), 20);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessFunctionASTest, PointerForwarding_NoUse) {
-    auto* src = R"(
-fn a(p : ptr<function, i32>) -> i32 {
-  return *p;
-}
-
-fn b(p : ptr<function, i32>) -> i32 {
-  return a(p);
-}
-)";
-
-    auto* expect =
-        R"(
-fn a_F(p : ptr<function, i32>) -> i32 {
-  return *(p);
-}
-
-fn b(p : ptr<function, i32>) -> i32 {
-  return a_F(p);
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src, EnableFunction());
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace function_as_tests
-
-////////////////////////////////////////////////////////////////////////////////
-// complex tests
-////////////////////////////////////////////////////////////////////////////////
-namespace complex_tests {
-
-using DirectVariableAccessComplexTest = TransformTest;
-
-TEST_F(DirectVariableAccessComplexTest, Param_ptr_mixed_vec4i32_ViaMultiple) {
-    auto* src = R"(
-struct str {
-  i : vec4<i32>,
-};
-
-@group(0) @binding(0) var<uniform> U     : vec4<i32>;
-@group(0) @binding(1) var<uniform> U_str   : str;
-@group(0) @binding(2) var<uniform> U_arr   : array<vec4<i32>, 8>;
-@group(0) @binding(3) var<uniform> U_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-@group(1) @binding(0) var<storage> S     : vec4<i32>;
-@group(1) @binding(1) var<storage> S_str   : str;
-@group(1) @binding(2) var<storage> S_arr   : array<vec4<i32>, 8>;
-@group(1) @binding(3) var<storage> S_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-          var<workgroup> W     : vec4<i32>;
-          var<workgroup> W_str   : str;
-          var<workgroup> W_arr   : array<vec4<i32>, 8>;
-          var<workgroup> W_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-fn fn_u(p : ptr<uniform, vec4<i32>>) -> vec4<i32> {
-  return *p;
-}
-
-fn fn_s(p : ptr<storage, vec4<i32>>) -> vec4<i32> {
-  return *p;
-}
-
-fn fn_w(p : ptr<workgroup, vec4<i32>>) -> vec4<i32> {
-  return *p;
-}
-
-fn b() {
-  let I = 3;
-  let J = 4;
-
-  let u           = fn_u(&U);
-  let u_str       = fn_u(&U_str.i);
-  let u_arr0      = fn_u(&U_arr[0]);
-  let u_arr1      = fn_u(&U_arr[1]);
-  let u_arrI      = fn_u(&U_arr[I]);
-  let u_arr1_arr0 = fn_u(&U_arr_arr[1][0]);
-  let u_arr2_arrI = fn_u(&U_arr_arr[2][I]);
-  let u_arrI_arr2 = fn_u(&U_arr_arr[I][2]);
-  let u_arrI_arrJ = fn_u(&U_arr_arr[I][J]);
-
-  let s           = fn_s(&S);
-  let s_str       = fn_s(&S_str.i);
-  let s_arr0      = fn_s(&S_arr[0]);
-  let s_arr1      = fn_s(&S_arr[1]);
-  let s_arrI      = fn_s(&S_arr[I]);
-  let s_arr1_arr0 = fn_s(&S_arr_arr[1][0]);
-  let s_arr2_arrI = fn_s(&S_arr_arr[2][I]);
-  let s_arrI_arr2 = fn_s(&S_arr_arr[I][2]);
-  let s_arrI_arrJ = fn_s(&S_arr_arr[I][J]);
-
-  let w           = fn_w(&W);
-  let w_str       = fn_w(&W_str.i);
-  let w_arr0      = fn_w(&W_arr[0]);
-  let w_arr1      = fn_w(&W_arr[1]);
-  let w_arrI      = fn_w(&W_arr[I]);
-  let w_arr1_arr0 = fn_w(&W_arr_arr[1][0]);
-  let w_arr2_arrI = fn_w(&W_arr_arr[2][I]);
-  let w_arrI_arr2 = fn_w(&W_arr_arr[I][2]);
-  let w_arrI_arrJ = fn_w(&W_arr_arr[I][J]);
-}
-)";
-
-    auto* expect = R"(
-struct str {
-  i : vec4<i32>,
-}
-
-@group(0) @binding(0) var<uniform> U : vec4<i32>;
-
-@group(0) @binding(1) var<uniform> U_str : str;
-
-@group(0) @binding(2) var<uniform> U_arr : array<vec4<i32>, 8>;
-
-@group(0) @binding(3) var<uniform> U_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-@group(1) @binding(0) var<storage> S : vec4<i32>;
-
-@group(1) @binding(1) var<storage> S_str : str;
-
-@group(1) @binding(2) var<storage> S_arr : array<vec4<i32>, 8>;
-
-@group(1) @binding(3) var<storage> S_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-var<workgroup> W : vec4<i32>;
-
-var<workgroup> W_str : str;
-
-var<workgroup> W_arr : array<vec4<i32>, 8>;
-
-var<workgroup> W_arr_arr : array<array<vec4<i32>, 8>, 4>;
-
-fn fn_u_U() -> vec4<i32> {
-  return U;
-}
-
-fn fn_u_U_str_i() -> vec4<i32> {
-  return U_str.i;
-}
-
-alias U_arr_X = array<u32, 1u>;
-
-fn fn_u_U_arr_X(p : U_arr_X) -> vec4<i32> {
-  return U_arr[p[0]];
-}
-
-alias U_arr_arr_X_X = array<u32, 2u>;
-
-fn fn_u_U_arr_arr_X_X(p : U_arr_arr_X_X) -> vec4<i32> {
-  return U_arr_arr[p[0]][p[1]];
-}
-
-fn fn_s_S() -> vec4<i32> {
-  return S;
-}
-
-fn fn_s_S_str_i() -> vec4<i32> {
-  return S_str.i;
-}
-
-alias S_arr_X = array<u32, 1u>;
-
-fn fn_s_S_arr_X(p : S_arr_X) -> vec4<i32> {
-  return S_arr[p[0]];
-}
-
-alias S_arr_arr_X_X = array<u32, 2u>;
-
-fn fn_s_S_arr_arr_X_X(p : S_arr_arr_X_X) -> vec4<i32> {
-  return S_arr_arr[p[0]][p[1]];
-}
-
-fn fn_w_W() -> vec4<i32> {
-  return W;
-}
-
-fn fn_w_W_str_i() -> vec4<i32> {
-  return W_str.i;
-}
-
-alias W_arr_X = array<u32, 1u>;
-
-fn fn_w_W_arr_X(p : W_arr_X) -> vec4<i32> {
-  return W_arr[p[0]];
-}
-
-alias W_arr_arr_X_X = array<u32, 2u>;
-
-fn fn_w_W_arr_arr_X_X(p : W_arr_arr_X_X) -> vec4<i32> {
-  return W_arr_arr[p[0]][p[1]];
-}
-
-fn b() {
-  let I = 3;
-  let J = 4;
-  let u = fn_u_U();
-  let u_str = fn_u_U_str_i();
-  let u_arr0 = fn_u_U_arr_X(U_arr_X(0));
-  let u_arr1 = fn_u_U_arr_X(U_arr_X(1));
-  let u_arrI = fn_u_U_arr_X(U_arr_X(u32(I)));
-  let u_arr1_arr0 = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(1, 0));
-  let u_arr2_arrI = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(2, u32(I)));
-  let u_arrI_arr2 = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(u32(I), 2));
-  let u_arrI_arrJ = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(u32(I), u32(J)));
-  let s = fn_s_S();
-  let s_str = fn_s_S_str_i();
-  let s_arr0 = fn_s_S_arr_X(S_arr_X(0));
-  let s_arr1 = fn_s_S_arr_X(S_arr_X(1));
-  let s_arrI = fn_s_S_arr_X(S_arr_X(u32(I)));
-  let s_arr1_arr0 = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(1, 0));
-  let s_arr2_arrI = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(2, u32(I)));
-  let s_arrI_arr2 = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(u32(I), 2));
-  let s_arrI_arrJ = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(u32(I), u32(J)));
-  let w = fn_w_W();
-  let w_str = fn_w_W_str_i();
-  let w_arr0 = fn_w_W_arr_X(W_arr_X(0));
-  let w_arr1 = fn_w_W_arr_X(W_arr_X(1));
-  let w_arrI = fn_w_W_arr_X(W_arr_X(u32(I)));
-  let w_arr1_arr0 = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(1, 0));
-  let w_arr2_arrI = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(2, u32(I)));
-  let w_arrI_arr2 = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(u32(I), 2));
-  let w_arrI_arrJ = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(u32(I), u32(J)));
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessComplexTest, Indexing) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-
-fn a(i : i32) -> i32 { return i; }
-
-fn b(p : ptr<storage, array<array<array<i32, 9>, 9>, 9>>) -> i32 {
-  return (*p) [ a( (*p)[0][1][2]    )]
-              [ a( (*p)[a(3)][4][5] )]
-              [ a( (*p)[6][a(7)][8] )];
-}
-
-fn c() {
-  let v = b(&S[42]);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-alias S_X = array<u32, 1u>;
-
-fn b_S_X(p : S_X) -> i32 {
-  return S[p[0]][a(S[p[0]][0][1][2])][a(S[p[0]][a(3)][4][5])][a(S[p[0]][6][a(7)][8])];
-}
-
-fn c() {
-  let v = b_S_X(S_X(42));
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessComplexTest, IndexingInPtrCall) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-
-fn a(pre : i32, i : ptr<storage, i32>, post : i32) -> i32 {
-  return *i;
-}
-
-fn b(p : ptr<storage, array<array<array<i32, 9>, 9>, 9>>) -> i32 {
-  return a(10, &(*p)[ a( 20, &(*p)[0][1][2], 30 )]
-                    [ a( 40, &(*p)[3][4][5], 50 )]
-                    [ a( 60, &(*p)[6][7][8], 70 )], 80);
-}
-
-fn c() {
-  let v = b(&S[42]);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-
-alias S_X_X_X_X = array<u32, 4u>;
-
-fn a_S_X_X_X_X(pre : i32, i : S_X_X_X_X, post : i32) -> i32 {
-  return S[i[0]][i[1]][i[2]][i[3]];
-}
-
-alias S_X = array<u32, 1u>;
-
-fn b_S_X(p : S_X) -> i32 {
-  return a_S_X_X_X_X(10, S_X_X_X_X(p[0u], u32(a_S_X_X_X_X(20, S_X_X_X_X(p[0u], 0, 1, 2), 30)), u32(a_S_X_X_X_X(40, S_X_X_X_X(p[0u], 3, 4, 5), 50)), u32(a_S_X_X_X_X(60, S_X_X_X_X(p[0u], 6, 7, 8), 70))), 80);
-}
-
-fn c() {
-  let v = b_S_X(S_X(42));
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DirectVariableAccessComplexTest, IndexingDualPointers) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<i32, 9>, 9>, 50>;
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9>, 9>, 50>;
-
-fn a(i : i32) -> i32 { return i; }
-
-fn b(s : ptr<storage, array<array<i32, 9>, 9>>,
-     u : ptr<uniform, array<array<vec4<i32>, 9>, 9>>) -> i32 {
-  return (*s) [ a( (*u)[0][1].x    )]
-              [ a( (*u)[a(3)][4].y )];
-}
-
-fn c() {
-  let v = b(&S[42], &U[24]);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage> S : array<array<array<i32, 9>, 9>, 50>;
-
-@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9>, 9>, 50>;
-
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-alias S_X = array<u32, 1u>;
-
-alias U_X = array<u32, 1u>;
-
-fn b_S_X_U_X(s : S_X, u : U_X) -> i32 {
-  return S[s[0]][a(U[u[0]][0][1].x)][a(U[u[0]][a(3)][4].y)];
-}
-
-fn c() {
-  let v = b_S_X_U_X(S_X(42), U_X(24));
-}
-)";
-
-    auto got = Run<DirectVariableAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace complex_tests
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.cc b/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.cc
deleted file mode 100644
index 0481710..0000000
--- a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.cc
+++ /dev/null
@@ -1,61 +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/wgsl/ast/transform/disable_uniformity_analysis.h"
-
-#include <utility>
-
-#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/module.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::DisableUniformityAnalysis);
-
-namespace tint::ast::transform {
-
-DisableUniformityAnalysis::DisableUniformityAnalysis() = default;
-
-DisableUniformityAnalysis::~DisableUniformityAnalysis() = default;
-
-Transform::ApplyResult DisableUniformityAnalysis::Apply(const Program& src,
-                                                        const DataMap&,
-                                                        DataMap&) const {
-    if (src.Sem().Module()->Extensions().Contains(
-            wgsl::Extension::kChromiumDisableUniformityAnalysis)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-    b.Enable(wgsl::Extension::kChromiumDisableUniformityAnalysis);
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h b/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h
deleted file mode 100644
index 052f59e..0000000
--- a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h
+++ /dev/null
@@ -1,51 +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_WGSL_AST_TRANSFORM_DISABLE_UNIFORMITY_ANALYSIS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_DISABLE_UNIFORMITY_ANALYSIS_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Disable uniformity analysis for the program.
-class DisableUniformityAnalysis final : public Castable<DisableUniformityAnalysis, Transform> {
-  public:
-    /// Constructor
-    DisableUniformityAnalysis();
-    /// Destructor
-    ~DisableUniformityAnalysis() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_DISABLE_UNIFORMITY_ANALYSIS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_test.cc b/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_test.cc
deleted file mode 100644
index 09c00b6..0000000
--- a/src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis_test.cc
+++ /dev/null
@@ -1,86 +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/wgsl/ast/transform/disable_uniformity_analysis.h"
-
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using DisableUniformityAnalysisTest = TransformTest;
-
-TEST_F(DisableUniformityAnalysisTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_TRUE(ShouldRun<DisableUniformityAnalysis>(src));
-}
-
-TEST_F(DisableUniformityAnalysisTest, ShouldRunExtensionAlreadyPresent) {
-    auto* src = R"(
-enable chromium_disable_uniformity_analysis;
-)";
-
-    EXPECT_FALSE(ShouldRun<DisableUniformityAnalysis>(src));
-}
-
-TEST_F(DisableUniformityAnalysisTest, EmptyModule) {
-    auto* src = R"()";
-
-    auto* expect = R"(
-enable chromium_disable_uniformity_analysis;
-)";
-
-    auto got = Run<DisableUniformityAnalysis>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DisableUniformityAnalysisTest, NonEmptyModule) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read> global : i32;
-
-@compute @workgroup_size(64)
-fn main() {
-  if ((global == 42)) {
-    workgroupBarrier();
-  }
-}
-)";
-
-    auto expect = "\nenable chromium_disable_uniformity_analysis;\n" + std::string(src);
-
-    auto got = Run<DisableUniformityAnalysis>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc
deleted file mode 100644
index d4c66c1..0000000
--- a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.cc
+++ /dev/null
@@ -1,210 +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/wgsl/ast/transform/expand_compound_assignment.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/compound_assignment_statement.h"
-#include "src/tint/lang/wgsl/ast/increment_decrement_statement.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.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/block_statement.h"
-#include "src/tint/lang/wgsl/sem/for_loop_statement.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ExpandCompoundAssignment);
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::ast::transform {
-
-namespace {
-
-bool ShouldRun(const Program& program) {
-    for (auto* node : program.ASTNodes().Objects()) {
-        if (node->IsAnyOf<CompoundAssignmentStatement, IncrementDecrementStatement>()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
-/// PIMPL state for the transform
-struct ExpandCompoundAssignment::State {
-    /// Constructor
-    /// @param context the clone context
-    explicit State(program::CloneContext& context)
-        : ctx(context), b(*ctx.dst), hoist_to_decl_before(ctx) {}
-
-    /// Replace `stmt` with a regular assignment statement of the form:
-    ///     lhs = lhs op rhs
-    /// The LHS expression will only be evaluated once, and any side effects will
-    /// be hoisted to `let` declarations above the assignment statement.
-    /// @param stmt the statement to replace
-    /// @param lhs the lhs expression from the source statement
-    /// @param rhs the rhs expression in the destination module
-    /// @param op the binary operator
-    void Expand(const Statement* stmt,
-                const Expression* lhs,
-                const Expression* rhs,
-                core::BinaryOp op) {
-        // Helper function to create the new LHS expression. This will be called
-        // twice when building the non-compound assignment statement, so must
-        // not produce expressions that cause side effects.
-        std::function<const Expression*()> new_lhs;
-
-        // Helper function to create a variable that is a pointer to `expr`.
-        auto hoist_pointer_to = [&](const Expression* expr) {
-            // Lhs may already be a pointer, in which case we don't take it's address
-            bool is_pointer = ctx.src->Sem().GetVal(expr)->Type()->Is<core::type::Pointer>();
-            auto name = b.Sym();
-            auto* ptr = is_pointer ? ctx.Clone(expr) : b.AddressOf(ctx.Clone(expr));
-            auto* decl = b.Decl(b.Let(name, ptr));
-            hoist_to_decl_before.InsertBefore(ctx.src->Sem().Get(stmt), decl);
-            return name;
-        };
-
-        // Helper function to hoist `expr` to a let declaration.
-        auto hoist_expr_to_let = [&](const Expression* expr) {
-            auto name = b.Sym();
-            auto* decl = b.Decl(b.Let(name, ctx.Clone(expr)));
-            hoist_to_decl_before.InsertBefore(ctx.src->Sem().Get(stmt), decl);
-            return name;
-        };
-
-        // Helper function that returns `true` if the type of `expr` is a vector.
-        auto is_vec = [&](const Expression* expr) {
-            if (auto* val_expr = ctx.src->Sem().GetVal(expr)) {
-                return val_expr->Type()->UnwrapPtrOrRef()->Is<core::type::Vector>();
-            }
-            return false;
-        };
-
-        // Hoist the LHS expression subtree into local constants to produce a new
-        // LHS that we can evaluate twice.
-        // We need to special case compound assignments to vector components since
-        // we cannot take the address of a vector component.
-        auto* index_accessor = lhs->As<IndexAccessorExpression>();
-        auto* member_accessor = lhs->As<MemberAccessorExpression>();
-        if (lhs->Is<IdentifierExpression>() ||
-            (member_accessor && member_accessor->object->Is<IdentifierExpression>())) {
-            // TODO(crbug.com/tint/2115): This branch should also handle (recursive) deref'd
-            // identifiers (e.g. (*p).bar += rhs)).
-
-            // This is the simple case with no side effects, so we can just use
-            // the original LHS expression directly. Before:
-            //     foo.bar += rhs;
-            // After:
-            //     foo.bar = foo.bar + rhs;
-            new_lhs = [&] { return ctx.Clone(lhs); };
-        } else if (index_accessor && is_vec(index_accessor->object)) {
-            // This is the case for vector component via an array accessor. We need
-            // to capture a pointer to the vector and also the index value.
-            // Before:
-            //     v[idx()] += rhs;
-            // After:
-            //     let vec_ptr = &v;
-            //     let index = idx();
-            //     (*vec_ptr)[index] = (*vec_ptr)[index] + rhs;
-            auto lhs_ptr = hoist_pointer_to(index_accessor->object);
-            auto index = hoist_expr_to_let(index_accessor->index);
-            new_lhs = [&, lhs_ptr, index] { return b.IndexAccessor(b.Deref(lhs_ptr), index); };
-        } else if (member_accessor && is_vec(member_accessor->object)) {
-            // This is the case for vector component via a member accessor. We just
-            // need to capture a pointer to the vector.
-            // Before:
-            //     a[idx()].y += rhs;
-            // After:
-            //     let vec_ptr = &a[idx()];
-            //     (*vec_ptr).y = (*vec_ptr).y + rhs;
-            auto lhs_ptr = hoist_pointer_to(member_accessor->object);
-            new_lhs = [&, lhs_ptr] {
-                return b.MemberAccessor(b.Deref(lhs_ptr), ctx.Clone(member_accessor->member));
-            };
-        } else {
-            // For all other statements that may have side-effecting expressions, we
-            // just need to capture a pointer to the whole LHS.
-            // Before:
-            //     a[idx()] += rhs;
-            // After:
-            //     let lhs_ptr = &a[idx()];
-            //     (*lhs_ptr) = (*lhs_ptr) + rhs;
-            auto lhs_ptr = hoist_pointer_to(lhs);
-            new_lhs = [&, lhs_ptr] { return b.Deref(lhs_ptr); };
-        }
-
-        // Replace the statement with a regular assignment statement.
-        auto* value = b.create<BinaryExpression>(op, new_lhs(), rhs);
-        ctx.Replace(stmt, b.Assign(new_lhs(), value));
-    }
-
-  private:
-    /// The clone context.
-    program::CloneContext& ctx;
-
-    /// The AST builder.
-    ast::Builder& b;
-
-    /// The HoistToDeclBefore helper instance.
-    HoistToDeclBefore hoist_to_decl_before;
-};
-
-ExpandCompoundAssignment::ExpandCompoundAssignment() = default;
-
-ExpandCompoundAssignment::~ExpandCompoundAssignment() = default;
-
-Transform::ApplyResult ExpandCompoundAssignment::Apply(const Program& src,
-                                                       const DataMap&,
-                                                       DataMap&) const {
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-    State state(ctx);
-    for (auto* node : src.ASTNodes().Objects()) {
-        if (auto* assign = node->As<CompoundAssignmentStatement>()) {
-            state.Expand(assign, assign->lhs, ctx.Clone(assign->rhs), assign->op);
-        } else if (auto* inc_dec = node->As<IncrementDecrementStatement>()) {
-            // For increment/decrement statements, `i++` becomes `i = i + 1`.
-            auto op = inc_dec->increment ? core::BinaryOp::kAdd : core::BinaryOp::kSubtract;
-            state.Expand(inc_dec, inc_dec->lhs, ctx.dst->Expr(1_a), op);
-        }
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h
deleted file mode 100644
index 2443128..0000000
--- a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment.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_WGSL_AST_TRANSFORM_EXPAND_COMPOUND_ASSIGNMENT_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_EXPAND_COMPOUND_ASSIGNMENT_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Converts compound assignment statements to regular assignment statements,
-/// hoisting the LHS expression if necessary.
-///
-/// Before:
-/// ```
-///   a += 1;
-///   vector_array[foo()][bar()] *= 2.0;
-/// ```
-///
-/// After:
-/// ```
-///   a = a + 1;
-///   let _vec = &vector_array[foo()];
-///   let _idx = bar();
-///   (*_vec)[_idx] = (*_vec)[_idx] * 2.0;
-/// ```
-///
-/// This transform also handles increment and decrement statements in the same
-/// manner, by replacing `i++` with `i = i + 1`.
-class ExpandCompoundAssignment final : public Castable<ExpandCompoundAssignment, Transform> {
-  public:
-    /// Constructor
-    ExpandCompoundAssignment();
-    /// Destructor
-    ~ExpandCompoundAssignment() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_EXPAND_COMPOUND_ASSIGNMENT_H_
diff --git a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_test.cc b/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_test.cc
deleted file mode 100644
index 8ffc045..0000000
--- a/src/tint/lang/wgsl/ast/transform/expand_compound_assignment_test.cc
+++ /dev/null
@@ -1,996 +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/wgsl/ast/transform/expand_compound_assignment.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using ExpandCompoundAssignmentTest = TransformTest;
-
-TEST_F(ExpandCompoundAssignmentTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<ExpandCompoundAssignment>(src));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, ShouldRunHasCompoundAssignment) {
-    auto* src = R"(
-fn foo() {
-  var v : i32;
-  v += 1;
-}
-)";
-
-    EXPECT_TRUE(ShouldRun<ExpandCompoundAssignment>(src));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, ShouldRunHasIncrementDecrement) {
-    auto* src = R"(
-fn foo() {
-  var v : i32;
-  v++;
-}
-)";
-
-    EXPECT_TRUE(ShouldRun<ExpandCompoundAssignment>(src));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Basic) {
-    auto* src = R"(
-fn main() {
-  var v : i32;
-  v += 1;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : i32;
-  v = (v + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsPointer) {
-    auto* src = R"(
-fn main() {
-  var v : i32;
-  let p = &v;
-  *p += 1;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : i32;
-  let p = &(v);
-  let tint_symbol = &(*(p));
-  *(tint_symbol) = (*(tint_symbol) + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsStructMember) {
-    auto* src = R"(
-struct S {
-  m : f32,
-}
-
-fn main() {
-  var s : S;
-  s.m += 1.0;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : f32,
-}
-
-fn main() {
-  var s : S;
-  s.m = (s.m + 1.0);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsArrayElement) {
-    auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn idx() -> i32 {
-  a[1] = 42;
-  return 1;
-}
-
-fn main() {
-  a[idx()] += 1;
-}
-)";
-
-    auto* expect = R"(
-var<private> a : array<i32, 4>;
-
-fn idx() -> i32 {
-  a[1] = 42;
-  return 1;
-}
-
-fn main() {
-  let tint_symbol = &(a[idx()]);
-  *(tint_symbol) = (*(tint_symbol) + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_ArrayAccessor) {
-    auto* src = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
-  v.y = 42;
-  return 1;
-}
-
-fn main() {
-  v[idx()] += 1;
-}
-)";
-
-    auto* expect = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
-  v.y = 42;
-  return 1;
-}
-
-fn main() {
-  let tint_symbol = &(v);
-  let tint_symbol_1 = idx();
-  (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor) {
-    auto* src = R"(
-fn main() {
-  var v : vec4<i32>;
-  v.y += 1;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : vec4<i32>;
-  v.y = (v.y + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsArrayOfVectorComponent_MemberAccessor_ViaArrayIndex) {
-    auto* src = R"(
-fn main() {
-  var v : array<vec4<i32>, 3>;
-  v[0].y += 1;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : array<vec4<i32>, 3>;
-  let tint_symbol = &(v[0]);
-  (*(tint_symbol)).y = ((*(tint_symbol)).y + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor_ViaDerefPointerDot) {
-    auto* src = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &v;
-  (*p).y += 1;
-}
-)";
-
-    // TODO(crbug.com/tint/2115): we currently needlessly hoist pointer-deref to another pointer.
-    auto* expect = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &(v);
-  let tint_symbol = &(*(p));
-  (*(tint_symbol)).y = ((*(tint_symbol)).y + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor_ViaPointerDot) {
-    auto* src = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &v;
-  p.y += 1;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &(v);
-  p.y = (p.y + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor_ViaDerefPointerIndex) {
-    auto* src = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &v;
-  (*p)[0] += 1;
-}
-)";
-
-    // TODO(crbug.com/tint/2115): we currently needlessly hoist pointer-deref to another pointer.
-    auto* expect = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &(v);
-  let tint_symbol = &(*(p));
-  let tint_symbol_1 = 0;
-  (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsVectorComponent_MemberAccessor_ViaPointerIndex) {
-    auto* src = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &v;
-  p[0] += 1;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &(v);
-  let tint_symbol = p;
-  let tint_symbol_1 = 0;
-  (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsMatrixColumn) {
-    auto* src = R"(
-var<private> m : mat4x4<f32>;
-
-fn idx() -> i32 {
-  m[0].y = 42.0;
-  return 1;
-}
-
-fn main() {
-  m[idx()] += 1.0;
-}
-)";
-
-    auto* expect = R"(
-var<private> m : mat4x4<f32>;
-
-fn idx() -> i32 {
-  m[0].y = 42.0;
-  return 1;
-}
-
-fn main() {
-  let tint_symbol = &(m[idx()]);
-  *(tint_symbol) = (*(tint_symbol) + 1.0);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsMatrixElement) {
-    auto* src = R"(
-var<private> m : mat4x4<f32>;
-
-fn idx1() -> i32 {
-  m[0].y = 42.0;
-  return 1;
-}
-
-fn idx2() -> i32 {
-  m[1].z = 42.0;
-  return 1;
-}
-
-fn main() {
-  m[idx1()][idx2()] += 1.0;
-}
-)";
-
-    auto* expect = R"(
-var<private> m : mat4x4<f32>;
-
-fn idx1() -> i32 {
-  m[0].y = 42.0;
-  return 1;
-}
-
-fn idx2() -> i32 {
-  m[1].z = 42.0;
-  return 1;
-}
-
-fn main() {
-  let tint_symbol = &(m[idx1()]);
-  let tint_symbol_1 = idx2();
-  (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1.0);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, LhsMultipleSideEffects) {
-    auto* src = R"(
-struct S {
-  a : array<vec4<f32>, 3>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : array<S>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
-  p += 1;
-  return 3;
-}
-
-fn idx2() -> i32 {
-  p *= 3;
-  return 2;
-}
-
-fn idx3() -> i32 {
-  p -= 2;
-  return 1;
-}
-
-fn main() {
-  buffer[idx1()].a[idx2()][idx3()] += 1.0;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : array<vec4<f32>, 3>,
-}
-
-@group(0) @binding(0) var<storage, read_write> buffer : array<S>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
-  p = (p + 1);
-  return 3;
-}
-
-fn idx2() -> i32 {
-  p = (p * 3);
-  return 2;
-}
-
-fn idx3() -> i32 {
-  p = (p - 2);
-  return 1;
-}
-
-fn main() {
-  let tint_symbol = &(buffer[idx1()].a[idx2()]);
-  let tint_symbol_1 = idx3();
-  (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1.0);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, ForLoopInit) {
-    auto* src = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
-  p = (p + 1);
-  return 3;
-}
-
-fn idx2() -> i32 {
-  p = (p * 3);
-  return 2;
-}
-
-fn main() {
-  for (a[idx1()][idx2()] += 1; ; ) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
-  p = (p + 1);
-  return 3;
-}
-
-fn idx2() -> i32 {
-  p = (p * 3);
-  return 2;
-}
-
-fn main() {
-  {
-    let tint_symbol = &(a[idx1()]);
-    let tint_symbol_1 = idx2();
-    (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-    loop {
-      {
-        break;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, ForLoopCont) {
-    auto* src = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
-  p = (p + 1);
-  return 3;
-}
-
-fn idx2() -> i32 {
-  p = (p * 3);
-  return 2;
-}
-
-fn main() {
-  for (; ; a[idx1()][idx2()] += 1) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
-  p = (p + 1);
-  return 3;
-}
-
-fn idx2() -> i32 {
-  p = (p * 3);
-  return 2;
-}
-
-fn main() {
-  loop {
-    {
-      break;
-    }
-
-    continuing {
-      let tint_symbol = &(a[idx1()]);
-      let tint_symbol_1 = idx2();
-      (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-    }
-  }
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_I32) {
-    auto* src = R"(
-fn main() {
-  var v : i32;
-  v++;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : i32;
-  v = (v + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_U32) {
-    auto* src = R"(
-fn main() {
-  var v : u32;
-  v++;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : u32;
-  v = (v + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Decrement_I32) {
-    auto* src = R"(
-fn main() {
-  var v : i32;
-  v--;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : i32;
-  v = (v - 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Decrement_U32) {
-    auto* src = R"(
-fn main() {
-  var v : u32;
-  v--;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : u32;
-  v = (v - 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsPointer) {
-    auto* src = R"(
-fn main() {
-  var v : i32;
-  let p = &v;
-  *p++;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : i32;
-  let p = &(v);
-  let tint_symbol = &(*(p));
-  *(tint_symbol) = (*(tint_symbol) + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsStructMember) {
-    auto* src = R"(
-struct S {
-  m : i32,
-}
-
-fn main() {
-  var s : S;
-  s.m++;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  m : i32,
-}
-
-fn main() {
-  var s : S;
-  s.m = (s.m + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsArrayElement) {
-    auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn idx() -> i32 {
-  a[1] = 42;
-  return 1;
-}
-
-fn main() {
-  a[idx()]++;
-}
-)";
-
-    auto* expect = R"(
-var<private> a : array<i32, 4>;
-
-fn idx() -> i32 {
-  a[1] = 42;
-  return 1;
-}
-
-fn main() {
-  let tint_symbol = &(a[idx()]);
-  *(tint_symbol) = (*(tint_symbol) + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsVectorComponent_ArrayAccessor) {
-    auto* src = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
-  v.y = 42;
-  return 1;
-}
-
-fn main() {
-  v[idx()]++;
-}
-)";
-
-    auto* expect = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
-  v.y = 42;
-  return 1;
-}
-
-fn main() {
-  let tint_symbol = &(v);
-  let tint_symbol_1 = idx();
-  (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest,
-       Increment_LhsVectorComponent_ArrayAccessor_ViaDerefPointerIndex) {
-    auto* src = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
-  v.y = 42;
-  return 1;
-}
-
-fn main() {
-  let p = &v;
-  (*p)[idx()]++;
-}
-)";
-
-    auto* expect = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
-  v.y = 42;
-  return 1;
-}
-
-fn main() {
-  let p = &(v);
-  let tint_symbol = &(*(p));
-  let tint_symbol_1 = idx();
-  (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsVectorComponent_ArrayAccessor_ViaPointerIndex) {
-    auto* src = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
-  v.y = 42;
-  return 1;
-}
-
-fn main() {
-  let p = &v;
-  p[idx()]++;
-}
-)";
-
-    auto* expect = R"(
-var<private> v : vec4<i32>;
-
-fn idx() -> i32 {
-  v.y = 42;
-  return 1;
-}
-
-fn main() {
-  let p = &(v);
-  let tint_symbol = p;
-  let tint_symbol_1 = idx();
-  (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsVectorComponent_MemberAccessor) {
-    auto* src = R"(
-fn main() {
-  var v : vec4<i32>;
-  v.y++;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : vec4<i32>;
-  v.y = (v.y + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest,
-       Increment_LhsVectorComponent_MemberAccessor_ViaDerefPointerDot) {
-    auto* src = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &v;
-  (*p).y++;
-}
-)";
-
-    // TODO(crbug.com/tint/2115): we currently needlessly hoist pointer-deref to another pointer.
-    auto* expect = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &(v);
-  let tint_symbol = &(*(p));
-  (*(tint_symbol)).y = ((*(tint_symbol)).y + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_LhsVectorComponent_MemberAccessor_ViaPointerDot) {
-    auto* src = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &v;
-  p.y++;
-}
-)";
-
-    auto* expect = R"(
-fn main() {
-  var v : vec4<i32>;
-  let p = &(v);
-  p.y = (p.y + 1);
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ExpandCompoundAssignmentTest, Increment_ForLoopCont) {
-    auto* src = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
-  p = (p + 1);
-  return 3;
-}
-
-fn idx2() -> i32 {
-  p = (p * 3);
-  return 2;
-}
-
-fn main() {
-  for (; ; a[idx1()][idx2()]++) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-var<private> a : array<vec4<i32>, 4>;
-
-var<private> p : i32;
-
-fn idx1() -> i32 {
-  p = (p + 1);
-  return 3;
-}
-
-fn idx2() -> i32 {
-  p = (p * 3);
-  return 2;
-}
-
-fn main() {
-  loop {
-    {
-      break;
-    }
-
-    continuing {
-      let tint_symbol = &(a[idx1()]);
-      let tint_symbol_1 = idx2();
-      (*(tint_symbol))[tint_symbol_1] = ((*(tint_symbol))[tint_symbol_1] + 1);
-    }
-  }
-}
-)";
-
-    auto got = Run<ExpandCompoundAssignment>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/first_index_offset.cc b/src/tint/lang/wgsl/ast/transform/first_index_offset.cc
deleted file mode 100644
index feaa575..0000000
--- a/src/tint/lang/wgsl/ast/transform/first_index_offset.cc
+++ /dev/null
@@ -1,189 +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/transform/first_index_offset.h"
-
-#include <memory>
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/lang/core/builtin_value.h"
-#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/function.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::FirstIndexOffset);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::FirstIndexOffset::BindingPoint);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::FirstIndexOffset::Data);
-
-namespace tint::ast::transform {
-namespace {
-
-// Uniform buffer member names
-constexpr char kFirstVertexName[] = "first_vertex_index";
-constexpr char kFirstInstanceName[] = "first_instance_index";
-
-bool ShouldRun(const Program& program) {
-    for (auto* fn : program.AST().Functions()) {
-        if (fn->PipelineStage() == PipelineStage::kVertex) {
-            return true;
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
-FirstIndexOffset::BindingPoint::BindingPoint() = default;
-FirstIndexOffset::BindingPoint::BindingPoint(uint32_t b, uint32_t g) : binding(b), group(g) {}
-FirstIndexOffset::BindingPoint::~BindingPoint() = default;
-
-FirstIndexOffset::Data::Data() = default;
-FirstIndexOffset::Data::Data(bool has_vtx_index, bool has_inst_index)
-    : has_vertex_index(has_vtx_index), has_instance_index(has_inst_index) {}
-FirstIndexOffset::Data::Data(const Data&) = default;
-FirstIndexOffset::Data::~Data() = default;
-
-FirstIndexOffset::FirstIndexOffset() = default;
-FirstIndexOffset::~FirstIndexOffset() = default;
-
-Transform::ApplyResult FirstIndexOffset::Apply(const Program& src,
-                                               const DataMap& inputs,
-                                               DataMap& outputs) const {
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    // Get the uniform buffer binding point
-    uint32_t ub_binding = binding_;
-    uint32_t ub_group = group_;
-    if (auto* binding_point = inputs.Get<BindingPoint>()) {
-        ub_binding = binding_point->binding;
-        ub_group = binding_point->group;
-    }
-
-    // Map of builtin usages
-    std::unordered_map<const sem::Variable*, const char*> builtin_vars;
-    std::unordered_map<const core::type::StructMember*, const char*> builtin_members;
-
-    bool has_vertex_index = false;
-    bool has_instance_index = false;
-
-    // Traverse the AST scanning for builtin accesses via variables (includes
-    // parameters) or structure member accesses.
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
-        if (auto* var = node->As<Variable>()) {
-            for (auto* attr : var->attributes) {
-                if (auto* builtin_attr = attr->As<BuiltinAttribute>()) {
-                    core::BuiltinValue builtin = builtin_attr->builtin;
-                    if (builtin == core::BuiltinValue::kVertexIndex) {
-                        auto* sem_var = ctx.src->Sem().Get(var);
-                        builtin_vars.emplace(sem_var, kFirstVertexName);
-                        has_vertex_index = true;
-                    }
-                    if (builtin == core::BuiltinValue::kInstanceIndex) {
-                        auto* sem_var = ctx.src->Sem().Get(var);
-                        builtin_vars.emplace(sem_var, kFirstInstanceName);
-                        has_instance_index = true;
-                    }
-                }
-            }
-        }
-        if (auto* member = node->As<StructMember>()) {
-            for (auto* attr : member->attributes) {
-                if (auto* builtin_attr = attr->As<BuiltinAttribute>()) {
-                    core::BuiltinValue builtin = builtin_attr->builtin;
-                    if (builtin == core::BuiltinValue::kVertexIndex) {
-                        auto* sem_mem = ctx.src->Sem().Get(member);
-                        builtin_members.emplace(sem_mem, kFirstVertexName);
-                        has_vertex_index = true;
-                    }
-                    if (builtin == core::BuiltinValue::kInstanceIndex) {
-                        auto* sem_mem = ctx.src->Sem().Get(member);
-                        builtin_members.emplace(sem_mem, kFirstInstanceName);
-                        has_instance_index = true;
-                    }
-                }
-            }
-        }
-    }
-
-    if (has_vertex_index || has_instance_index) {
-        // Add uniform buffer members and calculate byte offsets
-        tint::Vector<const StructMember*, 8> members;
-        members.Push(b.Member(kFirstVertexName, b.ty.u32()));
-        members.Push(b.Member(kFirstInstanceName, b.ty.u32()));
-        auto* struct_ = b.Structure(b.Sym(), std::move(members));
-
-        // Create a global to hold the uniform buffer
-        Symbol buffer_name = b.Sym();
-        b.GlobalVar(buffer_name, b.ty.Of(struct_), core::AddressSpace::kUniform,
-                    tint::Vector{
-                        b.Binding(AInt(ub_binding)),
-                        b.Group(AInt(ub_group)),
-                    });
-
-        // Fix up all references to the builtins with the offsets
-        ctx.ReplaceAll([=, &ctx](const Expression* expr) -> const Expression* {
-            if (auto* sem = ctx.src->Sem().GetVal(expr)) {
-                if (auto* user = sem->UnwrapLoad()->As<sem::VariableUser>()) {
-                    auto it = builtin_vars.find(user->Variable());
-                    if (it != builtin_vars.end()) {
-                        return ctx.dst->Add(ctx.CloneWithoutTransform(expr),
-                                            ctx.dst->MemberAccessor(buffer_name, it->second));
-                    }
-                }
-                if (auto* access = sem->As<sem::StructMemberAccess>()) {
-                    auto it = builtin_members.find(access->Member());
-                    if (it != builtin_members.end()) {
-                        return ctx.dst->Add(ctx.CloneWithoutTransform(expr),
-                                            ctx.dst->MemberAccessor(buffer_name, it->second));
-                    }
-                }
-            }
-            // Not interested in this expression. Just clone.
-            return nullptr;
-        });
-    }
-
-    outputs.Add<Data>(has_vertex_index, has_instance_index);
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/first_index_offset.h b/src/tint/lang/wgsl/ast/transform/first_index_offset.h
deleted file mode 100644
index 63598b9..0000000
--- a/src/tint/lang/wgsl/ast/transform/first_index_offset.h
+++ /dev/null
@@ -1,144 +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_WGSL_AST_TRANSFORM_FIRST_INDEX_OFFSET_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_FIRST_INDEX_OFFSET_H_
-
-#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// Adds firstVertex/Instance (injected via root constants) to
-/// vertex/instance index builtins.
-///
-/// This transform assumes that Name transform has been run before.
-///
-/// Unlike other APIs, D3D always starts vertex and instance numbering at 0,
-/// regardless of the firstVertex/Instance value specified. This transformer
-/// adds the value of firstVertex/Instance to each builtin. This action is
-/// performed by adding a new constant equal to original builtin +
-/// firstVertex/Instance to each function that references one of these builtins.
-///
-/// Note that D3D does not have any semantics for firstVertex/Instance.
-/// Therefore, these values must by passed to the shader.
-///
-/// Before:
-/// ```
-///   @builtin(vertex_index) var<in> vert_idx : u32;
-///   fn func() -> u32 {
-///     return vert_idx;
-///   }
-/// ```
-///
-/// After:
-/// ```
-///   struct TintFirstIndexOffsetData {
-///     tint_first_vertex_index : u32;
-///     tint_first_instance_index : u32;
-///   };
-///   @builtin(vertex_index) var<in> tint_first_index_offset_vert_idx : u32;
-///   @binding(N) @group(M) var<uniform> tint_first_index_data :
-///                                                    TintFirstIndexOffsetData;
-///   fn func() -> u32 {
-///     const vert_idx = (tint_first_index_offset_vert_idx +
-///                       tint_first_index_data.tint_first_vertex_index);
-///     return vert_idx;
-///   }
-/// ```
-///
-class FirstIndexOffset final : public Castable<FirstIndexOffset, Transform> {
-  public:
-    /// BindingPoint is consumed by the FirstIndexOffset transform.
-    /// BindingPoint specifies the binding point of the first index uniform
-    /// buffer.
-    struct BindingPoint final : public Castable<BindingPoint, Data> {
-        /// Constructor
-        BindingPoint();
-
-        /// Constructor
-        /// @param b the binding index
-        /// @param g the binding group
-        BindingPoint(uint32_t b, uint32_t g);
-
-        /// Destructor
-        ~BindingPoint() override;
-
-        /// `@binding()` for the first vertex / first instance uniform buffer
-        uint32_t binding = 0;
-        /// `@group()` for the first vertex / first instance uniform buffer
-        uint32_t group = 0;
-
-        /// Reflection for this struct
-        TINT_REFLECT(BindingPoint, binding, group);
-    };
-
-    /// Data is outputted by the FirstIndexOffset transform.
-    /// Data holds information about shader usage and constant buffer offsets.
-    struct Data final : public Castable<Data, transform::Data> {
-        /// Constructor
-        Data();
-
-        /// Constructor
-        /// @param has_vtx_index True if the shader uses vertex_index
-        /// @param has_inst_index True if the shader uses instance_index
-        Data(bool has_vtx_index, bool has_inst_index);
-
-        /// Copy constructor
-        Data(const Data&);
-
-        /// Destructor
-        ~Data() override;
-
-        /// True if the shader uses vertex_index
-        bool has_vertex_index = false;
-        /// True if the shader uses instance_index
-        bool has_instance_index = false;
-
-        /// Reflection for this struct
-        TINT_REFLECT(Data, has_vertex_index, has_instance_index);
-    };
-
-    /// Constructor
-    FirstIndexOffset();
-    /// Destructor
-    ~FirstIndexOffset() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    uint32_t binding_ = 0;
-    uint32_t group_ = 0;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_FIRST_INDEX_OFFSET_H_
diff --git a/src/tint/lang/wgsl/ast/transform/first_index_offset_test.cc b/src/tint/lang/wgsl/ast/transform/first_index_offset_test.cc
deleted file mode 100644
index ee0fd20..0000000
--- a/src/tint/lang/wgsl/ast/transform/first_index_offset_test.cc
+++ /dev/null
@@ -1,645 +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/transform/first_index_offset.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using FirstIndexOffsetTest = TransformTest;
-
-TEST_F(FirstIndexOffsetTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<FirstIndexOffset>(src));
-}
-
-TEST_F(FirstIndexOffsetTest, ShouldRunFragmentStage) {
-    auto* src = R"(
-@fragment
-fn entry() {
-  return;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<FirstIndexOffset>(src));
-}
-
-TEST_F(FirstIndexOffsetTest, ShouldRunVertexStage) {
-    auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    EXPECT_TRUE(ShouldRun<FirstIndexOffset>(src));
-}
-
-TEST_F(FirstIndexOffsetTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = "";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(0, 0);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    EXPECT_EQ(data, nullptr);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicVertexShader) {
-    auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-    auto* expect = src;
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(0, 0);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, false);
-    EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleVertexIndex) {
-    auto* src = R"(
-fn test(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  test(vert_idx);
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn test(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  test((vert_idx + tint_symbol_1.first_vertex_index));
-  return vec4<f32>();
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 2);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, true);
-    EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleVertexIndex_OutOfOrder) {
-    auto* src = R"(
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  test(vert_idx);
-  return vec4<f32>();
-}
-
-fn test(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  test((vert_idx + tint_symbol_1.first_vertex_index));
-  return vec4<f32>();
-}
-
-fn test(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 2);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, true);
-    EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex) {
-    auto* src = R"(
-fn test(inst_idx : u32) -> u32 {
-  return inst_idx;
-}
-
-@vertex
-fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  test(inst_idx);
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(7) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn test(inst_idx : u32) -> u32 {
-  return inst_idx;
-}
-
-@vertex
-fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  test((inst_idx + tint_symbol_1.first_instance_index));
-  return vec4<f32>();
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 7);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, false);
-    EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleInstanceIndex_OutOfOrder) {
-    auto* src = R"(
-@vertex
-fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  test(inst_idx);
-  return vec4<f32>();
-}
-
-fn test(inst_idx : u32) -> u32 {
-  return inst_idx;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(7) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  test((inst_idx + tint_symbol_1.first_instance_index));
-  return vec4<f32>();
-}
-
-fn test(inst_idx : u32) -> u32 {
-  return inst_idx;
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 7);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, false);
-    EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex) {
-    auto* src = R"(
-fn test(instance_idx : u32, vert_idx : u32) -> u32 {
-  return instance_idx + vert_idx;
-}
-
-struct Inputs {
-  @builtin(instance_index) instance_idx : u32,
-  @builtin(vertex_index) vert_idx : u32,
-};
-
-@vertex
-fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
-  test(inputs.instance_idx, inputs.vert_idx);
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn test(instance_idx : u32, vert_idx : u32) -> u32 {
-  return (instance_idx + vert_idx);
-}
-
-struct Inputs {
-  @builtin(instance_index)
-  instance_idx : u32,
-  @builtin(vertex_index)
-  vert_idx : u32,
-}
-
-@vertex
-fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
-  test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index));
-  return vec4<f32>();
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 2);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, true);
-    EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, BasicModuleBothIndex_OutOfOrder) {
-    auto* src = R"(
-@vertex
-fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
-  test(inputs.instance_idx, inputs.vert_idx);
-  return vec4<f32>();
-}
-
-struct Inputs {
-  @builtin(instance_index) instance_idx : u32,
-  @builtin(vertex_index) vert_idx : u32,
-};
-
-fn test(instance_idx : u32, vert_idx : u32) -> u32 {
-  return instance_idx + vert_idx;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> {
-  test((inputs.instance_idx + tint_symbol_1.first_instance_index), (inputs.vert_idx + tint_symbol_1.first_vertex_index));
-  return vec4<f32>();
-}
-
-struct Inputs {
-  @builtin(instance_index)
-  instance_idx : u32,
-  @builtin(vertex_index)
-  vert_idx : u32,
-}
-
-fn test(instance_idx : u32, vert_idx : u32) -> u32 {
-  return (instance_idx + vert_idx);
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 2);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, true);
-    EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, NestedCalls) {
-    auto* src = R"(
-fn func1(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-
-fn func2(vert_idx : u32) -> u32 {
-  return func1(vert_idx);
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  func2(vert_idx);
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn func1(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-
-fn func2(vert_idx : u32) -> u32 {
-  return func1(vert_idx);
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  func2((vert_idx + tint_symbol_1.first_vertex_index));
-  return vec4<f32>();
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 2);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, true);
-    EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, NestedCalls_OutOfOrder) {
-    auto* src = R"(
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  func2(vert_idx);
-  return vec4<f32>();
-}
-
-fn func2(vert_idx : u32) -> u32 {
-  return func1(vert_idx);
-}
-
-fn func1(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  func2((vert_idx + tint_symbol_1.first_vertex_index));
-  return vec4<f32>();
-}
-
-fn func2(vert_idx : u32) -> u32 {
-  return func1(vert_idx);
-}
-
-fn func1(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 2);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, true);
-    EXPECT_EQ(data->has_instance_index, false);
-}
-
-TEST_F(FirstIndexOffsetTest, MultipleEntryPoints) {
-    auto* src = R"(
-fn func(i : u32) -> u32 {
-  return i;
-}
-
-@vertex
-fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  func(vert_idx);
-  return vec4<f32>();
-}
-
-@vertex
-fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  func(vert_idx + inst_idx);
-  return vec4<f32>();
-}
-
-@vertex
-fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  func(inst_idx);
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-fn func(i : u32) -> u32 {
-  return i;
-}
-
-@vertex
-fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  func((vert_idx + tint_symbol_1.first_vertex_index));
-  return vec4<f32>();
-}
-
-@vertex
-fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index)));
-  return vec4<f32>();
-}
-
-@vertex
-fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  func((inst_idx + tint_symbol_1.first_instance_index));
-  return vec4<f32>();
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 2);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, true);
-    EXPECT_EQ(data->has_instance_index, true);
-}
-
-TEST_F(FirstIndexOffsetTest, MultipleEntryPoints_OutOfOrder) {
-    auto* src = R"(
-@vertex
-fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  func(vert_idx);
-  return vec4<f32>();
-}
-
-@vertex
-fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  func(vert_idx + inst_idx);
-  return vec4<f32>();
-}
-
-@vertex
-fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  func(inst_idx);
-  return vec4<f32>();
-}
-
-fn func(i : u32) -> u32 {
-  return i;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  first_vertex_index : u32,
-  first_instance_index : u32,
-}
-
-@binding(1) @group(2) var<uniform> tint_symbol_1 : tint_symbol;
-
-@vertex
-fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> {
-  func((vert_idx + tint_symbol_1.first_vertex_index));
-  return vec4<f32>();
-}
-
-@vertex
-fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  func(((vert_idx + tint_symbol_1.first_vertex_index) + (inst_idx + tint_symbol_1.first_instance_index)));
-  return vec4<f32>();
-}
-
-@vertex
-fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> {
-  func((inst_idx + tint_symbol_1.first_instance_index));
-  return vec4<f32>();
-}
-
-fn func(i : u32) -> u32 {
-  return i;
-}
-)";
-
-    DataMap config;
-    config.Add<FirstIndexOffset::BindingPoint>(1, 2);
-    auto got = Run<FirstIndexOffset>(src, std::move(config));
-
-    EXPECT_EQ(expect, str(got));
-
-    auto* data = got.data.Get<FirstIndexOffset::Data>();
-
-    ASSERT_NE(data, nullptr);
-    EXPECT_EQ(data->has_vertex_index, true);
-    EXPECT_EQ(data->has_instance_index, true);
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/fold_constants.cc b/src/tint/lang/wgsl/ast/transform/fold_constants.cc
deleted file mode 100644
index 7eee69c..0000000
--- a/src/tint/lang/wgsl/ast/transform/fold_constants.cc
+++ /dev/null
@@ -1,133 +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/wgsl/ast/transform/fold_constants.h"
-
-#include <utility>
-
-#include "src/tint/lang/core/constant/splat.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/abstract_float.h"
-#include "src/tint/lang/core/type/abstract_int.h"
-#include "src/tint/lang/core/type/bool.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/utils/rtti/switch.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::FoldConstants);
-
-namespace tint::ast::transform {
-namespace {
-
-struct State {
-    enum class Splat {
-        kAllowed,
-        kDisallowed,
-    };
-
-    const ast::Expression* Constant(const core::constant::Value* c) {
-        auto composite = [&](Splat splat) -> const ast::Expression* {
-            auto ty = FoldConstants::CreateASTTypeFor(ctx, c->Type());
-            if (c->AllZero()) {
-                return b.Call(ty);
-            }
-            if (splat == Splat::kAllowed && c->Is<core::constant::Splat>()) {
-                return b.Call(ty, Constant(c->Index(0)));
-            }
-
-            Vector<const ast::Expression*, 8> els;
-            for (size_t i = 0, n = c->NumElements(); i < n; i++) {
-                els.Push(Constant(c->Index(i)));
-            }
-            return b.Call(ty, std::move(els));
-        };
-
-        return tint::Switch(
-            c->Type(),  //
-            [&](const core::type::AbstractFloat*) { return b.Expr(c->ValueAs<AFloat>()); },
-            [&](const core::type::AbstractInt*) { return b.Expr(c->ValueAs<AInt>()); },
-            [&](const core::type::I32*) { return b.Expr(c->ValueAs<i32>()); },
-            [&](const core::type::U32*) { return b.Expr(c->ValueAs<u32>()); },
-            [&](const core::type::F32*) { return b.Expr(c->ValueAs<f32>()); },
-            [&](const core::type::F16*) { return b.Expr(c->ValueAs<f16>()); },
-            [&](const core::type::Bool*) { return b.Expr(c->ValueAs<bool>()); },
-            [&](const core::type::Array*) { return composite(Splat::kDisallowed); },
-            [&](const core::type::Vector*) { return composite(Splat::kAllowed); },
-            [&](const core::type::Matrix*) { return composite(Splat::kDisallowed); },
-            [&](const core::type::Struct*) { return composite(Splat::kDisallowed); },
-            TINT_ICE_ON_NO_MATCH);
-    }
-
-    Transform::ApplyResult Run() {
-        ctx.ReplaceAll([&](const Expression* expr) -> const Expression* {
-            auto& sem = ctx.src->Sem();
-            auto* ve = sem.Get<sem::ValueExpression>(expr);
-
-            // No value expression SEM node found
-            if (!ve) {
-                return nullptr;
-            }
-
-            auto* cv = ve->ConstantValue();
-
-            // No constant value for this expression
-            if (!cv) {
-                return nullptr;
-            }
-
-            if (cv->Type()->IsAbstract() && !cv->Type()->IsFloatScalar() &&
-                !cv->Type()->IsSignedIntegerScalar() && !cv->Type()->IsUnsignedIntegerScalar()) {
-                return nullptr;
-            }
-
-            return Constant(cv);
-        });
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-    const Program& src;
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-};
-
-}  // namespace
-
-FoldConstants::FoldConstants() = default;
-
-FoldConstants::~FoldConstants() = default;
-
-Transform::ApplyResult FoldConstants::Apply(const Program& src, const DataMap&, DataMap&) const {
-    State s{src, {}};
-    return s.Run();
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/fold_constants.h b/src/tint/lang/wgsl/ast/transform/fold_constants.h
deleted file mode 100644
index c9057cd..0000000
--- a/src/tint/lang/wgsl/ast/transform/fold_constants.h
+++ /dev/null
@@ -1,66 +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_WGSL_AST_TRANSFORM_FOLD_CONSTANTS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_FOLD_CONSTANTS_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// A transform that replaces constant expressions with the constant values.
-///
-/// # Example
-/// ```
-/// const a = false && true;
-/// const b = sin(1);
-/// ```
-///
-/// ```
-/// const a = false;
-/// const b = 0.841470;
-/// ```
-class FoldConstants final : public Castable<FoldConstants, Transform> {
-  public:
-    /// Constructor
-    FoldConstants();
-
-    /// Destructor
-    ~FoldConstants() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outpus) const override;
-
-  private:
-    const ast::Expression* Constant(const core::constant::Value* c);
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_FOLD_CONSTANTS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/fold_constants_test.cc b/src/tint/lang/wgsl/ast/transform/fold_constants_test.cc
deleted file mode 100644
index 081f90f..0000000
--- a/src/tint/lang/wgsl/ast/transform/fold_constants_test.cc
+++ /dev/null
@@ -1,440 +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/wgsl/ast/transform/fold_constants.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using FoldConstantsTest = TransformTest;
-
-TEST_F(FoldConstantsTest, NoFolding) {
-    auto* src = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, Expression) {
-    auto* src = R"(
-fn foo() -> i32 {
-  return (1 + (2 * 4)) - (3 * 5);
-}
-)";
-
-    auto* expect = R"(
-fn foo() -> i32 {
-  return -6i;
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, Abstract) {
-    auto* src = R"(
-fn foo() {
-  const v = vec4(1, 2, 3, vec3(0)[1 + 1 - 0]);
-}
-)";
-
-    auto* expect = R"(
-fn foo() {
-  const v = vec4(1, 2, 3, 0);
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, IndexExpression) {
-    auto* src = R"(
-struct S {
-  a : array<i32>,
-}
-
-@group(0) @binding(0) var<storage> s : S;
-
-fn foo() -> i32 {
-  return s.a[(3 * 5) - (1 + (2 * 4))];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : array<i32>,
-}
-
-@group(0i) @binding(0i) var<storage> s : S;
-
-fn foo() -> i32 {
-  return s.a[6i];
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, IndexAccessorToConst) {
-    auto* src = R"(
-const a : array<i32, 4> = array(0, 1, 2, 3);
-
-fn foo() -> i32 {
-  return a[3];
-}
-)";
-
-    auto* expect = R"(
-const a : array<i32, 4i> = array<i32, 4u>(0i, 1i, 2i, 3i);
-
-fn foo() -> i32 {
-  return 3i;
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, SplatParam) {
-    auto* src = R"(
-fn foo() -> i32 {
-  let v = vec3(2 + 5 - 4);
-  return v.x;
-}
-)";
-
-    auto* expect = R"(
-fn foo() -> i32 {
-  let v = vec3<i32>(3i);
-  return v.x;
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, CallParam) {
-    auto* src = R"(
-fn foo() -> i32 {
-  let v = vec3(2 + 5 - 4, 5 * 9, -2 + -3);
-  return v.x;
-}
-)";
-
-    auto* expect = R"(
-fn foo() -> i32 {
-  let v = vec3<i32>(3i, 45i, -5i);
-  return v.x;
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, LHSIndexExpressionOnAssign) {
-    auto* src = R"(
-fn foo() -> i32 {
-  var v = vec4(0);
-  v[1 * 2 + (3 - 2)] = 1;
-
-  return v[1];
-}
-)";
-
-    auto* expect = R"(
-fn foo() -> i32 {
-  var v = vec4<i32>();
-  v[3i] = 1i;
-  return v[1i];
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, LHSIndexExpressionOnCompoundAssign) {
-    auto* src = R"(
-fn foo() -> i32 {
-  var v = vec4(0);
-  v[1 * 2 + (3 - 2)] += 1;
-
-  return v[1];
-}
-)";
-
-    auto* expect = R"(
-fn foo() -> i32 {
-  var v = vec4<i32>();
-  v[3i] += 1i;
-  return v[1i];
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, LHSIndexExpressionOnIncrement) {
-    auto* src = R"(
-fn foo() -> i32 {
-  var v = vec4(0);
-  v[1 * 2 + (3 - 2)]++;
-
-  return v[1];
-}
-)";
-
-    auto* expect = R"(
-fn foo() -> i32 {
-  var v = vec4<i32>();
-  v[3i]++;
-  return v[1i];
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, Attribute) {
-    auto* src = R"(
-struct A {
-  @location(1 + 2)
-  v : i32,
-}
-
-@group(0) @binding(0) var<storage> a : A;
-
-fn foo() -> i32 {
-  return a.v;
-}
-)";
-
-    auto* expect = R"(
-struct A {
-  @location(3i)
-  v : i32,
-}
-
-@group(0i) @binding(0i) var<storage> a : A;
-
-fn foo() -> i32 {
-  return a.v;
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, ShortCircut) {
-    auto* src = R"(
-fn foo() -> bool {
-  return false && (1 + 2) == 3;
-}
-)";
-
-    auto* expect = R"(
-fn foo() -> bool {
-  return false;
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, BreakIf) {
-    auto* src = R"(
-fn foo() {
-  loop {
-
-    continuing {
-      break if 4 > 3;
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn foo() {
-  loop {
-
-    continuing {
-      break if true;
-    }
-  }
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, Switch) {
-    auto* src = R"(
-fn foo() {
-  switch(1 + 2 + (3 * 5)) {
-    case 1 + 2, 3 + 4: {
-      break;
-    }
-    default: {
-      break;
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn foo() {
-  switch(18i) {
-    case 3i, 7i: {
-      break;
-    }
-    default: {
-      break;
-    }
-  }
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, If) {
-    auto* src = R"(
-fn foo() {
-  if 4 - 2 > 3 {
-    return;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn foo() {
-  if (false) {
-    return;
-  }
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, For) {
-    auto* src = R"(
-fn foo() {
-  for(var i = 2 + (3 * 4); i < 9 + (5 * 10); i = i + (2 * 3)) {
-  }
-}
-)";
-
-    auto* expect = R"(
-fn foo() {
-  for(var i = 14i; (i < 59i); i = (i + 6i)) {
-  }
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, While) {
-    auto* src = R"(
-fn foo() {
-  while(2 > 4) {
-  }
-}
-)";
-
-    auto* expect = R"(
-fn foo() {
-  while(false) {
-  }
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, ConstAssert) {
-    auto* src = R"(
-const_assert 2 < (3 + 5);
-)";
-
-    auto* expect = R"(
-const_assert true;
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(FoldConstantsTest, InternalStruct) {
-    auto* src = R"(
-fn foo() {
-  let a = modf(1.5);
-}
-)";
-
-    auto* expect = R"(
-fn foo() {
-  let a = __modf_result_f32(0.5f, 1.0f);
-}
-)";
-
-    auto got = Run<FoldConstants>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/get_insertion_point.cc b/src/tint/lang/wgsl/ast/transform/get_insertion_point.cc
deleted file mode 100644
index 4f0d9c7..0000000
--- a/src/tint/lang/wgsl/ast/transform/get_insertion_point.cc
+++ /dev/null
@@ -1,68 +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/wgsl/ast/transform/get_insertion_point.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/program/program.h"
-#include "src/tint/lang/wgsl/sem/for_loop_statement.h"
-#include "src/tint/utils/diagnostic/diagnostic.h"
-#include "src/tint/utils/ice/ice.h"
-#include "src/tint/utils/rtti/switch.h"
-
-namespace tint::ast::transform::utils {
-
-InsertionPoint GetInsertionPoint(program::CloneContext& ctx, const Statement* stmt) {
-    auto& sem = ctx.src->Sem();
-    using RetType = std::pair<const sem::BlockStatement*, const Statement*>;
-
-    if (auto* sem_stmt = sem.Get(stmt)) {
-        auto* parent = sem_stmt->Parent();
-        return Switch(
-            parent,
-            [&](const sem::BlockStatement* block) -> RetType {
-                // Common case, can insert in the current block above/below the input
-                // statement.
-                return {block, stmt};
-            },
-            [&](const sem::ForLoopStatement* fl) -> RetType {
-                // `stmt` is either the for loop initializer or the continuing
-                // statement of a for-loop.
-                if (fl->Declaration()->initializer == stmt) {
-                    // For loop init, can insert above the for loop itself.
-                    return {fl->Block(), fl->Declaration()};
-                }
-
-                // Cannot insert before or after continuing statement of a for-loop
-                return {};
-            },  //
-            TINT_ICE_ON_NO_MATCH);
-    }
-
-    return {};
-}
-
-}  // namespace tint::ast::transform::utils
diff --git a/src/tint/lang/wgsl/ast/transform/get_insertion_point.h b/src/tint/lang/wgsl/ast/transform/get_insertion_point.h
deleted file mode 100644
index 2c1658e..0000000
--- a/src/tint/lang/wgsl/ast/transform/get_insertion_point.h
+++ /dev/null
@@ -1,57 +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_WGSL_AST_TRANSFORM_GET_INSERTION_POINT_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_GET_INSERTION_POINT_H_
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/builder.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-
-// Forward declarations
-namespace tint::program {
-class CloneContext;
-}
-
-namespace tint::ast::transform::utils {
-
-/// InsertionPoint is a pair of the block (`first`) within which, and the
-/// statement (`second`) before or after which to insert.
-using InsertionPoint = std::pair<const sem::BlockStatement*, const Statement*>;
-
-/// For the input statement, returns the block and statement within that
-/// block to insert before/after. If `stmt` is a for-loop continue statement,
-/// the function returns {nullptr, nullptr} as we cannot insert before/after it.
-/// @param ctx the clone context
-/// @param stmt the statement to insert before or after
-/// @return the insertion point
-InsertionPoint GetInsertionPoint(program::CloneContext& ctx, const Statement* stmt);
-
-}  // namespace tint::ast::transform::utils
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_GET_INSERTION_POINT_H_
diff --git a/src/tint/lang/wgsl/ast/transform/get_insertion_point_test.cc b/src/tint/lang/wgsl/ast/transform/get_insertion_point_test.cc
deleted file mode 100644
index 4c20059..0000000
--- a/src/tint/lang/wgsl/ast/transform/get_insertion_point_test.cc
+++ /dev/null
@@ -1,108 +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 <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/get_insertion_point.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.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/utils/ice/ice.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::ast::transform {
-namespace {
-
-using GetInsertionPointTest = ::testing::Test;
-
-TEST_F(GetInsertionPointTest, Block) {
-    // fn f() {
-    //     var a = 1i;
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Expr(1_i);
-    auto* var = b.Decl(b.Var("a", expr));
-    auto* block = b.Block(var);
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{block});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    // Can insert in block containing the variable, above or below the input statement.
-    auto ip = utils::GetInsertionPoint(ctx, var);
-    ASSERT_EQ(ip.first->Declaration(), block);
-    ASSERT_EQ(ip.second, var);
-}
-
-TEST_F(GetInsertionPointTest, ForLoopInit) {
-    // fn f() {
-    //     for(var a = 1i; true; ) {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Expr(1_i);
-    auto* var = b.Decl(b.Var("a", expr));
-    auto* fl = b.For(var, b.Expr(true), nullptr, b.Block());
-    auto* func_block = b.Block(fl);
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{func_block});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    // Can insert in block containing for-loop above the for-loop itself.
-    auto ip = utils::GetInsertionPoint(ctx, var);
-    ASSERT_EQ(ip.first->Declaration(), func_block);
-    ASSERT_EQ(ip.second, fl);
-}
-
-TEST_F(GetInsertionPointTest, ForLoopCont_Invalid) {
-    // fn f() {
-    //     for(; true; var a = 1i) {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Expr(1_i);
-    auto* var = b.Decl(b.Var("a", expr));
-    auto* s = b.For({}, b.Expr(true), var, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    // Can't insert before/after for loop continue statement (would ned to be converted to loop).
-    auto ip = utils::GetInsertionPoint(ctx, var);
-    ASSERT_EQ(ip.first, nullptr);
-    ASSERT_EQ(ip.second, nullptr);
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.cc b/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.cc
deleted file mode 100644
index 1a506ec..0000000
--- a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.cc
+++ /dev/null
@@ -1,453 +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/wgsl/ast/transform/hoist_to_decl_before.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/builder.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/sem/block_statement.h"
-#include "src/tint/lang/wgsl/sem/for_loop_statement.h"
-#include "src/tint/lang/wgsl/sem/if_statement.h"
-#include "src/tint/lang/wgsl/sem/while_statement.h"
-#include "src/tint/utils/containers/hashmap.h"
-#include "src/tint/utils/containers/transform.h"
-
-namespace tint::ast::transform {
-
-/// Private implementation of HoistToDeclBefore transform
-struct HoistToDeclBefore::State {
-    /// Constructor
-    /// @param ctx_in the clone context
-    explicit State(program::CloneContext& ctx_in) : ctx(ctx_in), b(*ctx_in.dst) {}
-
-    /// @copydoc HoistToDeclBefore::Add()
-    bool Add(const sem::ValueExpression* before_expr,
-             const Expression* expr,
-             VariableKind kind,
-             const char* decl_name) {
-        auto name = b.Symbols().New(decl_name);
-
-        switch (kind) {
-            case VariableKind::kLet: {
-                auto* ty = ctx.src->Sem().GetVal(expr)->Type();
-                TINT_ASSERT(!ty->IsAbstract());
-                auto builder = [this, expr, name, ty] {
-                    return b.Decl(b.Let(name, Transform::CreateASTTypeFor(ctx, ty),
-                                        ctx.CloneWithoutTransform(expr)));
-                };
-                if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
-                    return false;
-                }
-                break;
-            }
-
-            case VariableKind::kVar: {
-                auto* ty = ctx.src->Sem().GetVal(expr)->Type();
-                TINT_ASSERT(!ty->IsAbstract());
-                auto builder = [this, expr, name, ty] {
-                    return b.Decl(b.Var(name, Transform::CreateASTTypeFor(ctx, ty),
-                                        ctx.CloneWithoutTransform(expr)));
-                };
-                if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
-                    return false;
-                }
-                break;
-            }
-
-            case VariableKind::kConst: {
-                auto builder = [this, expr, name] {
-                    return b.Decl(b.Const(name, ctx.CloneWithoutTransform(expr)));
-                };
-                if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
-                    return false;
-                }
-                break;
-            }
-        }
-
-        // Replace the source expression with a reference to the hoisted declaration.
-        ctx.Replace(expr, b.Expr(name));
-        return true;
-    }
-
-    /// @copydoc HoistToDeclBefore::InsertBefore(const sem::Statement*, const Statement*)
-    bool InsertBefore(const sem::Statement* before_stmt, const Statement* stmt) {
-        if (stmt) {
-            auto builder = [stmt] { return stmt; };
-            return InsertBeforeImpl(before_stmt, std::move(builder));
-        }
-        return InsertBeforeImpl(before_stmt, Decompose{});
-    }
-
-    /// @copydoc HoistToDeclBefore::InsertBefore(const sem::Statement*, const StmtBuilder&)
-    bool InsertBefore(const sem::Statement* before_stmt, const StmtBuilder& builder) {
-        return InsertBeforeImpl(before_stmt, std::move(builder));
-    }
-
-    /// @copydoc HoistToDeclBefore::Replace(const sem::Statement* what, const Statement* with)
-    bool Replace(const sem::Statement* what, const Statement* with) {
-        auto builder = [with] { return with; };
-        return Replace(what, std::move(builder));
-    }
-
-    /// @copydoc HoistToDeclBefore::Replace(const sem::Statement* what, const StmtBuilder& with)
-    bool Replace(const sem::Statement* what, const StmtBuilder& with) {
-        if (!InsertBeforeImpl(what, Decompose{})) {
-            return false;
-        }
-        ctx.Replace(what->Declaration(), with);
-        return true;
-    }
-
-    /// @copydoc HoistToDeclBefore::Prepare()
-    bool Prepare(const sem::ValueExpression* before_expr) {
-        return InsertBefore(before_expr->Stmt(), nullptr);
-    }
-
-  private:
-    program::CloneContext& ctx;
-    ast::Builder& b;
-
-    /// Holds information about a for-loop that needs to be decomposed into a
-    /// loop, so that declaration statements can be inserted before the
-    /// condition expression or continuing statement.
-    struct LoopInfo {
-        Vector<StmtBuilder, 8> init_decls;
-        Vector<StmtBuilder, 8> cond_decls;
-        Vector<StmtBuilder, 8> cont_decls;
-    };
-
-    /// Info for each else-if that needs decomposing
-    struct ElseIfInfo {
-        /// Decls to insert before condition
-        Vector<StmtBuilder, 8> cond_decls;
-    };
-
-    /// For-loops that need to be decomposed to loops.
-    Hashmap<const sem::ForLoopStatement*, LoopInfo, 4> for_loops;
-
-    /// Whiles that need to be decomposed to loops.
-    Hashmap<const sem::WhileStatement*, LoopInfo, 4> while_loops;
-
-    /// 'else if' statements that need to be decomposed to 'else {if}'
-    Hashmap<const IfStatement*, ElseIfInfo, 4> else_ifs;
-
-    template <size_t N>
-    static auto Build(const Vector<StmtBuilder, N>& builders) {
-        return tint::Transform(builders, [&](auto& builder) { return builder(); });
-    }
-
-    /// @returns a new LoopInfo reference for the given @p for_loop.
-    /// @note if this is the first call to this method, then RegisterForLoopTransform() is
-    /// automatically called.
-    /// @warning the returned reference is invalid if this is called a second time, or the
-    /// #for_loops map is mutated.
-    LoopInfo& ForLoop(const sem::ForLoopStatement* for_loop) {
-        if (for_loops.IsEmpty()) {
-            RegisterForLoopTransform();
-        }
-        return for_loops.GetOrAddZero(for_loop);
-    }
-
-    /// @returns a new LoopInfo reference for the given @p while_loop.
-    /// @note if this is the first call to this method, then RegisterWhileLoopTransform() is
-    /// automatically called.
-    /// @warning the returned reference is invalid if this is called a second time, or the
-    /// #for_loops map is mutated.
-    LoopInfo& WhileLoop(const sem::WhileStatement* while_loop) {
-        if (while_loops.IsEmpty()) {
-            RegisterWhileLoopTransform();
-        }
-        return while_loops.GetOrAddZero(while_loop);
-    }
-
-    /// @returns a new ElseIfInfo reference for the given @p else_if.
-    /// @note if this is the first call to this method, then RegisterElseIfTransform() is
-    /// automatically called.
-    /// @warning the returned reference is invalid if this is called a second time, or the
-    /// #else_ifs map is mutated.
-    ElseIfInfo& ElseIf(const IfStatement* else_if) {
-        if (else_ifs.IsEmpty()) {
-            RegisterElseIfTransform();
-        }
-        return else_ifs.GetOrAddZero(else_if);
-    }
-
-    /// Registers the handler for transforming for-loops based on the content of the #for_loops map.
-    void RegisterForLoopTransform() const {
-        ctx.ReplaceAll([&](const ForLoopStatement* stmt) -> const Statement* {
-            auto& sem = ctx.src->Sem();
-
-            if (auto* fl = sem.Get(stmt)) {
-                if (auto info = for_loops.Get(fl)) {
-                    auto* for_loop = fl->Declaration();
-                    // For-loop needs to be decomposed to a loop.
-                    // Build the loop body's statements.
-                    // Start with any let declarations for the conditional expression.
-                    auto body_stmts = Build(info->cond_decls);
-                    // If the for-loop has a condition, emit this next as:
-                    //   if (!cond) { break; }
-                    if (auto* cond = for_loop->condition) {
-                        // !condition
-                        auto* not_cond =
-                            b.create<UnaryOpExpression>(core::UnaryOp::kNot, ctx.Clone(cond));
-                        // { break; }
-                        auto* break_body = b.Block(b.create<BreakStatement>());
-                        // if (!condition) { break; }
-                        body_stmts.Push(b.If(not_cond, break_body));
-                    }
-                    // Next emit the for-loop body
-                    body_stmts.Push(ctx.Clone(for_loop->body));
-
-                    // Create the continuing block if there was one.
-                    const BlockStatement* continuing = nullptr;
-                    if (auto* cont = for_loop->continuing) {
-                        // Continuing block starts with any let declarations used by
-                        // the continuing.
-                        auto cont_stmts = Build(info->cont_decls);
-                        cont_stmts.Push(ctx.Clone(cont));
-                        continuing = b.Block(cont_stmts);
-                    }
-
-                    auto* body = b.Block(body_stmts);
-                    auto* loop = b.Loop(body, continuing);
-
-                    // If the loop has no initializer statements, then we're done.
-                    // Otherwise, wrap loop with another block, prefixed with the initializer
-                    // statements
-                    if (!info->init_decls.IsEmpty() || for_loop->initializer) {
-                        auto stmts = Build(info->init_decls);
-                        if (auto* init = for_loop->initializer) {
-                            stmts.Push(ctx.Clone(init));
-                        }
-                        stmts.Push(loop);
-                        return b.Block(std::move(stmts));
-                    }
-                    return loop;
-                }
-            }
-            return nullptr;
-        });
-    }
-
-    /// Registers the handler for transforming while-loops based on the content of the #while_loops
-    /// map.
-    void RegisterWhileLoopTransform() const {
-        // At least one while needs to be transformed into a loop.
-        ctx.ReplaceAll([&](const WhileStatement* stmt) -> const Statement* {
-            auto& sem = ctx.src->Sem();
-
-            if (auto* w = sem.Get(stmt)) {
-                if (auto info = while_loops.Get(w)) {
-                    auto* while_loop = w->Declaration();
-                    // While needs to be decomposed to a loop.
-                    // Build the loop body's statements.
-                    // Start with any let declarations for the conditional
-                    // expression.
-                    auto body_stmts =
-                        tint::Transform(info->cond_decls, [&](auto& builder) { return builder(); });
-                    // Emit the condition as:
-                    //   if (!cond) { break; }
-                    auto* cond = while_loop->condition;
-                    // !condition
-                    auto* not_cond = b.Not(ctx.Clone(cond));
-                    // { break; }
-                    auto* break_body = b.Block(b.Break());
-                    // if (!condition) { break; }
-                    body_stmts.Push(b.If(not_cond, break_body));
-
-                    // Next emit the body
-                    body_stmts.Push(ctx.Clone(while_loop->body));
-
-                    const BlockStatement* continuing = nullptr;
-
-                    auto* body = b.Block(body_stmts);
-                    auto* loop = b.Loop(body, continuing);
-                    return loop;
-                }
-            }
-            return nullptr;
-        });
-    }
-
-    /// Registers the handler for transforming if-statements based on the content of the #else_ifs
-    /// map.
-    void RegisterElseIfTransform() const {
-        // Decompose 'else-if' statements into 'else { if }' blocks.
-        ctx.ReplaceAll([&](const IfStatement* stmt) -> const Statement* {
-            if (auto info = else_ifs.Get(stmt)) {
-                // Build the else block's body statements, starting with let decls for the
-                // conditional expression.
-                auto body_stmts = Build(info->cond_decls);
-
-                // Move the 'else-if' into the new `else` block as a plain 'if'.
-                auto* cond = ctx.Clone(stmt->condition);
-                auto* body = ctx.Clone(stmt->body);
-                auto* new_if = b.If(cond, body, b.Else(ctx.Clone(stmt->else_statement)));
-                body_stmts.Push(new_if);
-
-                // Replace the 'else-if' with the new 'else' block.
-                return b.Block(body_stmts);
-            }
-            return nullptr;
-        });
-    }
-
-    /// A type used to signal to InsertBeforeImpl that no insertion should take place - instead flow
-    /// control statements should just be decomposed.
-    struct Decompose {};
-
-    template <typename BUILDER>
-    bool InsertBeforeImpl(const sem::Statement* before_stmt, BUILDER&& builder) {
-        (void)builder;  // Avoid 'unused parameter' warning due to 'if constexpr'
-
-        auto* ip = before_stmt->Declaration();
-
-        auto* else_if = before_stmt->As<sem::IfStatement>();
-        if (else_if && else_if->Parent()->Is<sem::IfStatement>()) {
-            // Insertion point is an 'else if' condition.
-            // Need to convert 'else if' to 'else { if }'.
-            auto& else_if_info = ElseIf(else_if->Declaration());
-
-            // Index the map to decompose this else if, even if `stmt` is nullptr.
-            auto& decls = else_if_info.cond_decls;
-            if constexpr (!std::is_same_v<BUILDER, Decompose>) {
-                decls.Push(std::forward<BUILDER>(builder));
-            }
-            return true;
-        }
-
-        if (auto* fl = before_stmt->As<sem::ForLoopStatement>()) {
-            // Insertion point is a for-loop condition.
-            // For-loop needs to be decomposed to a loop.
-
-            // Index the map to decompose this for-loop, even if `stmt` is nullptr.
-            auto& decls = ForLoop(fl).cond_decls;
-            if constexpr (!std::is_same_v<BUILDER, Decompose>) {
-                decls.Push(std::forward<BUILDER>(builder));
-            }
-            return true;
-        }
-
-        if (auto* w = before_stmt->As<sem::WhileStatement>()) {
-            // Insertion point is a while condition.
-            // While needs to be decomposed to a loop.
-
-            // Index the map to decompose this while, even if `stmt` is nullptr.
-            auto& decls = WhileLoop(w).cond_decls;
-            if constexpr (!std::is_same_v<BUILDER, Decompose>) {
-                decls.Push(std::forward<BUILDER>(builder));
-            }
-            return true;
-        }
-
-        auto* parent = before_stmt->Parent();  // The statement's parent
-        if (auto* block = parent->As<sem::BlockStatement>()) {
-            // Insert point sits in a block. Simple case.
-            // Insert the stmt before the parent statement.
-            if constexpr (!std::is_same_v<BUILDER, Decompose>) {
-                ctx.InsertBefore(block->Declaration()->statements, ip,
-                                 std::forward<BUILDER>(builder));
-            }
-            return true;
-        }
-
-        auto* fl = parent->As<sem::ForLoopStatement>();
-        if (DAWN_LIKELY(fl)) {
-            // Insertion point is a for-loop initializer or continuing statement.
-            // These require special care.
-            if (fl->Declaration()->initializer == ip) {
-                // Insertion point is a for-loop initializer.
-                // For-loop needs to be decomposed to a loop.
-
-                // Index the map to decompose this for-loop, even if `stmt` is nullptr.
-                auto& decls = ForLoop(fl).init_decls;
-                if constexpr (!std::is_same_v<BUILDER, Decompose>) {
-                    decls.Push(std::forward<BUILDER>(builder));
-                }
-
-                return true;
-            }
-
-            if (DAWN_LIKELY(fl->Declaration()->continuing == ip)) {
-                // Insertion point is a for-loop continuing statement.
-                // For-loop needs to be decomposed to a loop.
-
-                // Index the map to decompose this for-loop, even if `stmt` is nullptr.
-                auto& decls = ForLoop(fl).cont_decls;
-                if constexpr (!std::is_same_v<BUILDER, Decompose>) {
-                    decls.Push(std::forward<BUILDER>(builder));
-                }
-
-                return true;
-            }
-
-            TINT_ICE() << "unhandled use of expression in for-loop";
-            return false;
-        }
-
-        TINT_ICE() << "unhandled expression parent statement type: " << parent->TypeInfo().name;
-        return false;
-    }
-};
-
-HoistToDeclBefore::HoistToDeclBefore(program::CloneContext& ctx)
-    : state_(std::make_unique<State>(ctx)) {}
-
-HoistToDeclBefore::~HoistToDeclBefore() {}
-
-bool HoistToDeclBefore::Add(const sem::ValueExpression* before_expr,
-                            const Expression* expr,
-                            VariableKind kind,
-                            const char* decl_name) {
-    return state_->Add(before_expr, expr, kind, decl_name);
-}
-
-bool HoistToDeclBefore::InsertBefore(const sem::Statement* before_stmt, const Statement* stmt) {
-    return state_->InsertBefore(before_stmt, stmt);
-}
-
-bool HoistToDeclBefore::InsertBefore(const sem::Statement* before_stmt,
-                                     const StmtBuilder& builder) {
-    return state_->InsertBefore(before_stmt, builder);
-}
-
-bool HoistToDeclBefore::Replace(const sem::Statement* what, const Statement* with) {
-    return state_->Replace(what, with);
-}
-
-bool HoistToDeclBefore::Replace(const sem::Statement* what, const StmtBuilder& with) {
-    return state_->Replace(what, with);
-}
-
-bool HoistToDeclBefore::Prepare(const sem::ValueExpression* before_expr) {
-    return state_->Prepare(before_expr);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h b/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h
deleted file mode 100644
index cbe90e1..0000000
--- a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h
+++ /dev/null
@@ -1,120 +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_WGSL_AST_TRANSFORM_HOIST_TO_DECL_BEFORE_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_HOIST_TO_DECL_BEFORE_H_
-
-#include <functional>
-#include <memory>
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-
-namespace tint::ast::transform {
-
-/// Utility class that can be used to hoist expressions before other
-/// expressions, possibly converting 'for-loop's to 'loop's and 'else-if's to
-// 'else {if}'s.
-class HoistToDeclBefore {
-  public:
-    /// Constructor
-    /// @param ctx the clone context
-    explicit HoistToDeclBefore(program::CloneContext& ctx);
-
-    /// Destructor
-    ~HoistToDeclBefore();
-
-    /// StmtBuilder is a builder of an AST statement
-    using StmtBuilder = std::function<const Statement*()>;
-
-    /// VariableKind is either a var, let or const
-    enum class VariableKind {
-        kVar,
-        kLet,
-        kConst,
-    };
-
-    /// Hoists @p expr to a `let` or `var` with optional `decl_name`, inserting it
-    /// before @p before_expr.
-    /// @param before_expr expression to insert `expr` before
-    /// @param expr expression to hoist
-    /// @param kind variable kind to hoist to
-    /// @param decl_name optional name to use for the variable/constant name
-    /// @return true on success
-    bool Add(const sem::ValueExpression* before_expr,
-             const Expression* expr,
-             VariableKind kind,
-             const char* decl_name = "");
-
-    /// Inserts @p stmt before @p before_stmt, possibly converting 'for-loop's to 'loop's if
-    /// necessary.
-    /// @warning If the container of @p before_stmt is cloned multiple times, then the resolver will
-    /// ICE as the same statement cannot be shared.
-    /// @param before_stmt statement to insert @p stmt before
-    /// @param stmt statement to insert
-    /// @return true on success
-    bool InsertBefore(const sem::Statement* before_stmt, const Statement* stmt);
-
-    /// Inserts the returned statement of @p builder before @p before_stmt, possibly converting
-    /// 'for-loop's to 'loop's if necessary.
-    /// @note If the container of @p before_stmt is cloned multiple times, then @p builder will be
-    /// called for each clone.
-    /// @param before_stmt the preceding statement that the statement of @p builder will be inserted
-    /// before
-    /// @param builder the statement builder used to create the new statement
-    /// @return true on success
-    bool InsertBefore(const sem::Statement* before_stmt, const StmtBuilder& builder);
-
-    /// Replaces the statement @p what with the statement @p stmt, possibly converting 'for-loop's
-    /// to 'loop's if necessary.
-    /// @param what the statement to replace
-    /// @param with the replacement statement
-    /// @return true on success
-    bool Replace(const sem::Statement* what, const Statement* with);
-
-    /// Replaces the statement @p what with the statement returned by @p stmt, possibly converting
-    /// 'for-loop's to 'loop's if necessary.
-    /// @param what the statement to replace
-    /// @param with the replacement statement builder
-    /// @return true on success
-    bool Replace(const sem::Statement* what, const StmtBuilder& with);
-
-    /// Use to signal that we plan on hoisting a decl before `before_expr`. This
-    /// will convert 'for-loop's to 'loop's and 'else-if's to 'else {if}'s if
-    /// needed.
-    /// @param before_expr expression we would hoist a decl before
-    /// @return true on success
-    bool Prepare(const sem::ValueExpression* before_expr);
-
-  private:
-    struct State;
-    std::unique_ptr<State> state_;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_HOIST_TO_DECL_BEFORE_H_
diff --git a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc b/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
deleted file mode 100644
index 36f03a9..0000000
--- a/src/tint/lang/wgsl/ast/transform/hoist_to_decl_before_test.cc
+++ /dev/null
@@ -1,1156 +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 <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.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/if_statement.h"
-#include "src/tint/lang/wgsl/sem/index_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::ast::transform {
-namespace {
-
-using HoistToDeclBeforeTest = ::testing::Test;
-
-TEST_F(HoistToDeclBeforeTest, VarInit) {
-    // fn f() {
-    //     var a = 1;
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Expr(1_i);
-    auto* var = b.Decl(b.Var("a", expr));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  let tint_symbol : i32 = 1i;
-  var a = tint_symbol;
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, ForLoopInit) {
-    // fn f() {
-    //     for(var a = 1i; true; ) {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Expr(1_i);
-    auto* s = b.For(b.Decl(b.Var("a", expr)), b.Expr(true), nullptr, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  {
-    var tint_symbol : i32 = 1i;
-    var a = tint_symbol;
-    loop {
-      if (!(true)) {
-        break;
-      }
-      {
-      }
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, ForLoopCond) {
-    // fn f() {
-    //     const a = true;
-    //     for(; a; ) {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* var = b.Decl(b.Const("a", b.Expr(true)));
-    auto* expr = b.Expr("a");
-    auto* s = b.For(nullptr, expr, nullptr, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().GetVal(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kConst);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  const a = true;
-  loop {
-    const tint_symbol = a;
-    if (!(tint_symbol)) {
-      break;
-    }
-    {
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, ForLoopCont) {
-    // fn f() {
-    //     for(; true; var a = 1i) {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Expr(1_i);
-    auto* s = b.For(nullptr, b.Expr(true), b.Decl(b.Var("a", expr)), b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  loop {
-    if (!(true)) {
-      break;
-    }
-    {
-    }
-
-    continuing {
-      let tint_symbol : i32 = 1i;
-      var a = tint_symbol;
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, WhileCond) {
-    // fn f() {
-    //     var a : bool;
-    //     while(a) {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* var = b.Decl(b.Var("a", b.ty.bool_()));
-    auto* expr = b.Expr("a");
-    auto* s = b.While(expr, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().GetVal(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  var a : bool;
-  loop {
-    var tint_symbol : bool = a;
-    if (!(tint_symbol)) {
-      break;
-    }
-    {
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, ElseIf) {
-    // fn f() {
-    //     const a = true;
-    //     if (true) {
-    //     } else if (a) {
-    //     } else {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* var = b.Decl(b.Const("a", b.Expr(true)));
-    auto* expr = b.Expr("a");
-    auto* s = b.If(b.Expr(true), b.Block(),      //
-                   b.Else(b.If(expr, b.Block(),  //
-                               b.Else(b.Block()))));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().GetVal(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kConst);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  const a = true;
-  if (true) {
-  } else {
-    const tint_symbol = a;
-    if (tint_symbol) {
-    } else {
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Array1D) {
-    // fn f() {
-    //     var a : array<i32, 10>;
-    //     var b = a[0];
-    // }
-    ProgramBuilder b;
-    auto* var1 = b.Decl(b.Var("a", b.ty.array<i32, 10>()));
-    auto* expr = b.IndexAccessor("a", 0_i);
-    auto* var2 = b.Decl(b.Var("b", expr));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var1, var2});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  var a : array<i32, 10u>;
-  let tint_symbol : i32 = a[0i];
-  var b = tint_symbol;
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Array2D) {
-    // fn f() {
-    //     var a : array<array<i32, 10>, 10>;
-    //     var b = a[0][0];
-    // }
-    ProgramBuilder b;
-
-    auto* var1 = b.Decl(b.Var("a", b.ty.array(b.ty.array<i32, 10>(), 10_i)));
-    auto* expr = b.IndexAccessor(b.IndexAccessor("a", 0_i), 0_i);
-    auto* var2 = b.Decl(b.Var("b", expr));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var1, var2});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  var a : array<array<i32, 10u>, 10i>;
-  var tint_symbol : i32 = a[0i][0i];
-  var b = tint_symbol;
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Prepare_ForLoopCond) {
-    // fn f() {
-    //     var a : bool;
-    //     for(; a; ) {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* var = b.Decl(b.Var("a", b.ty.bool_()));
-    auto* expr = b.Expr("a");
-    auto* s = b.For(nullptr, expr, nullptr, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().GetVal(expr);
-    hoistToDeclBefore.Prepare(sem_expr);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  var a : bool;
-  loop {
-    if (!(a)) {
-      break;
-    }
-    {
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Prepare_ForLoopCont) {
-    // fn f() {
-    //     for(; true; var a = 1i) {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Expr(1_i);
-    auto* s = b.For(nullptr, b.Expr(true), b.Decl(b.Var("a", expr)), b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Prepare(sem_expr);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  loop {
-    if (!(true)) {
-      break;
-    }
-    {
-    }
-
-    continuing {
-      var a = 1i;
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Prepare_ElseIf) {
-    // fn f() {
-    //     var a : bool;
-    //     if (true) {
-    //     } else if (a) {
-    //     } else {
-    //     }
-    // }
-    ProgramBuilder b;
-    auto* var = b.Decl(b.Var("a", b.ty.bool_()));
-    auto* expr = b.Expr("a");
-    auto* s = b.If(b.Expr(true), b.Block(),      //
-                   b.Else(b.If(expr, b.Block(),  //
-                               b.Else(b.Block()))));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().GetVal(expr);
-    hoistToDeclBefore.Prepare(sem_expr);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  var a : bool;
-  if (true) {
-  } else {
-    if (a) {
-    } else {
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_Block) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a = 1i;
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* before_stmt = ctx.src->Sem().Get(var);
-    auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
-    hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  foo();
-  var a = 1i;
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_Block_Function) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a = 1i;
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* before_stmt = ctx.src->Sem().Get(var);
-    hoistToDeclBefore.InsertBefore(before_stmt,
-                                   [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  foo();
-  var a = 1i;
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopInit) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     for(var a = 1i; true;) {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* before_stmt = ctx.src->Sem().Get(var);
-    auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
-    hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  {
-    foo();
-    var a = 1i;
-    loop {
-      if (!(true)) {
-        break;
-      }
-      {
-      }
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopInit_Function) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     for(var a = 1i; true;) {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* before_stmt = ctx.src->Sem().Get(var);
-    hoistToDeclBefore.InsertBefore(before_stmt,
-                                   [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  {
-    foo();
-    var a = 1i;
-    loop {
-      if (!(true)) {
-        break;
-      }
-      {
-      }
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopCont) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a = 1i;
-    //     for(; true; a+=1i) {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
-    auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* before_stmt = ctx.src->Sem().Get(cont->As<Statement>());
-    auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
-    hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  var a = 1i;
-  loop {
-    if (!(true)) {
-      break;
-    }
-    {
-    }
-
-    continuing {
-      foo();
-      a += 1i;
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopCont_Function) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a = 1i;
-    //     for(; true; a+=1i) {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
-    auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* before_stmt = ctx.src->Sem().Get(cont->As<Statement>());
-    hoistToDeclBefore.InsertBefore(before_stmt,
-                                   [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  var a = 1i;
-  loop {
-    if (!(true)) {
-      break;
-    }
-    {
-    }
-
-    continuing {
-      foo();
-      a += 1i;
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ElseIf) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a : bool;
-    //     if (true) {
-    //     } else if (a) {
-    //     } else {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.ty.bool_()));
-    auto* elseif = b.If(b.Expr("a"), b.Block(), b.Else(b.Block()));
-    auto* s = b.If(b.Expr(true), b.Block(),  //
-                   b.Else(elseif));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* before_stmt = ctx.src->Sem().Get(elseif);
-    auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
-    hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  var a : bool;
-  if (true) {
-  } else {
-    foo();
-    if (a) {
-    } else {
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, InsertBefore_ElseIf_Function) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a : bool;
-    //     if (true) {
-    //     } else if (a) {
-    //     } else {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.ty.bool_()));
-    auto* elseif = b.If(b.Expr("a"), b.Block(), b.Else(b.Block()));
-    auto* s = b.If(b.Expr(true), b.Block(),  //
-                   b.Else(elseif));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* before_stmt = ctx.src->Sem().Get(elseif);
-    hoistToDeclBefore.InsertBefore(before_stmt,
-                                   [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  var a : bool;
-  if (true) {
-  } else {
-    foo();
-    if (a) {
-    } else {
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, AbstractArray_ToLet) {
-    // fn f() {
-    //     var a : array<f32, 1> = array(1);
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Call(b.ty("array"), b.Expr(1_a));
-    auto* var = b.Decl(b.Var("a", b.ty.array(b.ty.f32(), 1_a), expr));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kLet);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  let tint_symbol : array<f32, 1u> = array(1);
-  var a : array<f32, 1> = tint_symbol;
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, AbstractArray_ToVar) {
-    // fn f() {
-    //     var a : array<f32, 1> = array(1);
-    // }
-    ProgramBuilder b;
-    auto* expr = b.Call(b.ty("array"), b.Expr(1_a));
-    auto* var = b.Decl(b.Var("a", b.ty.array(b.ty.f32(), 1_a), expr));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* sem_expr = ctx.src->Sem().Get(expr);
-    hoistToDeclBefore.Add(sem_expr, expr, HoistToDeclBefore::VariableKind::kVar);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn f() {
-  var tint_symbol : array<f32, 1u> = array(1);
-  var a : array<f32, 1> = tint_symbol;
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_Block) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a = 1i;
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* target_stmt = ctx.src->Sem().Get(var);
-    auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
-    hoistToDeclBefore.Replace(target_stmt, new_stmt);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  foo();
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_Block_Function) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a = 1i;
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* target_stmt = ctx.src->Sem().Get(var);
-    hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  foo();
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_ForLoopInit) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     for(var a = 1i; true;) {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* target_stmt = ctx.src->Sem().Get(var);
-    auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
-    hoistToDeclBefore.Replace(target_stmt, new_stmt);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  {
-    foo();
-    loop {
-      if (!(true)) {
-        break;
-      }
-      {
-      }
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_ForLoopInit_Function) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     for(var a = 1i; true;) {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* target_stmt = ctx.src->Sem().Get(var);
-    hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  {
-    foo();
-    loop {
-      if (!(true)) {
-        break;
-      }
-      {
-      }
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_ForLoopCont) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a = 1i;
-    //     for(; true; a+=1i) {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
-    auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* target_stmt = ctx.src->Sem().Get(cont->As<Statement>());
-    auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
-    hoistToDeclBefore.Replace(target_stmt, new_stmt);
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  var a = 1i;
-  loop {
-    if (!(true)) {
-      break;
-    }
-    {
-    }
-
-    continuing {
-      foo();
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-TEST_F(HoistToDeclBeforeTest, Replace_ForLoopCont_Function) {
-    // fn foo() {
-    // }
-    // fn f() {
-    //     var a = 1i;
-    //     for(; true; a+=1i) {
-    //     }
-    // }
-    ProgramBuilder b;
-    b.Func("foo", tint::Empty, b.ty.void_(), tint::Empty);
-    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
-    auto* cont = b.CompoundAssign("a", b.Expr(1_i), core::BinaryOp::kAdd);
-    auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
-    b.Func("f", tint::Empty, b.ty.void_(), Vector{var, s});
-
-    Program original(resolver::Resolve(b));
-    ProgramBuilder cloned_b;
-    program::CloneContext ctx(&cloned_b, &original);
-
-    HoistToDeclBefore hoistToDeclBefore(ctx);
-    auto* target_stmt = ctx.src->Sem().Get(cont->As<Statement>());
-    hoistToDeclBefore.Replace(target_stmt, [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
-
-    ctx.Clone();
-    Program cloned(resolver::Resolve(cloned_b));
-
-    auto* expect = R"(
-fn foo() {
-}
-
-fn f() {
-  var a = 1i;
-  loop {
-    if (!(true)) {
-      break;
-    }
-    {
-    }
-
-    continuing {
-      foo();
-    }
-  }
-}
-)";
-
-    EXPECT_EQ(expect, str(cloned));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc
deleted file mode 100644
index 6d40cf3..0000000
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.cc
+++ /dev/null
@@ -1,614 +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/wgsl/ast/transform/multiplanar_external_texture.h"
-
-#include <string>
-#include <vector>
-
-#include "src/tint/lang/core/type/texture_dimension.h"
-#include "src/tint/lang/wgsl/ast/function.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/variable.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::MultiplanarExternalTexture);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::MultiplanarExternalTexture::NewBindingPoints);
-
-namespace tint::ast::transform {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-bool ShouldRun(const Program& program) {
-    auto ext = program.Types().Find<core::type::ExternalTexture>();
-    return ext != nullptr;
-}
-
-/// This struct stores symbols for new bindings created as a result of transforming a
-/// texture_external instance.
-struct NewBindingSymbols {
-    Symbol params;
-    Symbol plane_0;
-    Symbol plane_1;
-};
-}  // namespace
-
-/// PIMPL state for the transform
-struct MultiplanarExternalTexture::State {
-    /// The clone context.
-    program::CloneContext& ctx;
-
-    /// Alias to `*ctx.dst`
-    ast::Builder& b;
-
-    /// Destination binding locations for the expanded texture_external provided
-    /// as input into the transform.
-    const NewBindingPoints* new_binding_points;
-
-    /// Symbol for the GammaTransferParams
-    Symbol gamma_transfer_struct_sym;
-
-    /// Symbol for the ExternalTextureParams struct
-    Symbol params_struct_sym;
-
-    /// Symbol for the textureLoadExternal functions
-    Hashmap<const sem::CallTarget*, Symbol, 2> texture_load_external_fns;
-
-    /// Symbol for the textureSampleExternal function
-    Symbol texture_sample_external_sym;
-
-    /// Symbol for the textureSampleExternalDEPRECATED function
-    Symbol texture_sample_external_deprecated_sym;
-
-    /// Symbol for the gammaCorrection function
-    Symbol gamma_correction_sym;
-
-    /// Storage for new bindings that have been created corresponding to an original
-    /// texture_external binding.
-    std::unordered_map<const sem::Variable*, NewBindingSymbols> new_binding_symbols;
-
-    /// Constructor
-    /// @param context the clone
-    /// @param newBindingPoints the input destination binding locations for the
-    /// expanded texture_external
-    State(program::CloneContext& context, const NewBindingPoints* newBindingPoints)
-        : ctx(context), b(*context.dst), new_binding_points(newBindingPoints) {}
-
-    /// Processes the module
-    void Process() {
-        auto& sem = ctx.src->Sem();
-
-        // For each texture_external binding, we replace it with a texture_2d<f32> binding and
-        // create two additional bindings (one texture_2d<f32> to represent the secondary plane and
-        // one uniform buffer for the ExternalTextureParams struct).
-        for (auto* global : ctx.src->AST().GlobalVariables()) {
-            auto* sem_var = sem.Get<sem::GlobalVariable>(global);
-            if (!sem_var->Type()->UnwrapRef()->Is<core::type::ExternalTexture>()) {
-                continue;
-            }
-
-            // If the attributes are empty, then this must be a texture_external passed as a
-            // function parameter. These variables are transformed elsewhere.
-            if (global->attributes.IsEmpty()) {
-                continue;
-            }
-
-            // If we find a texture_external binding, we know we must emit the ExternalTextureParams
-            // struct.
-            if (!params_struct_sym.IsValid()) {
-                createExtTexParamsStructs();
-            }
-
-            // The binding points for the newly introduced bindings must have been provided to this
-            // transform. We fetch the new binding points by providing the original texture_external
-            // binding points into the passed map.
-            BindingPoint bp = *sem_var->Attributes().binding_point;
-
-            const tint::transform::multiplanar::BindingsMap::const_iterator it =
-                new_binding_points->bindings_map.find(bp);
-            if (it == new_binding_points->bindings_map.end()) {
-                b.Diagnostics().AddError(Source{})
-                    << "missing new binding points for texture_external at binding {" << bp.group
-                    << "," << bp.binding << "}";
-                continue;
-            }
-
-            const tint::transform::multiplanar::BindingPoints bps = it->second;
-
-            // Symbols for the newly created bindings must be saved so they can be passed as
-            // parameters later. These are placed in a map and keyed by the source symbol associated
-            // with the texture_external binding that corresponds with the new destination bindings.
-            // NewBindingSymbols new_binding_syms;
-            auto& syms = new_binding_symbols[sem_var];
-            syms.plane_0 = ctx.Clone(global->name->symbol);
-            syms.plane_1 = b.Symbols().New("ext_tex_plane_1");
-            if (new_binding_points->allow_collisions) {
-                b.GlobalVar(syms.plane_1,
-                            b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()),
-                            b.Disable(DisabledValidation::kBindingPointCollision),
-                            b.Group(AInt(bps.plane_1.group)), b.Binding(AInt(bps.plane_1.binding)));
-            } else {
-                b.GlobalVar(syms.plane_1,
-                            b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()),
-                            b.Group(AInt(bps.plane_1.group)), b.Binding(AInt(bps.plane_1.binding)));
-            }
-            syms.params = b.Symbols().New("ext_tex_params");
-            if (new_binding_points->allow_collisions) {
-                b.GlobalVar(syms.params, b.ty("ExternalTextureParams"),
-                            core::AddressSpace::kUniform,
-                            b.Disable(DisabledValidation::kBindingPointCollision),
-                            b.Group(AInt(bps.params.group)), b.Binding(AInt(bps.params.binding)));
-            } else {
-                b.GlobalVar(syms.params, b.ty("ExternalTextureParams"),
-                            core::AddressSpace::kUniform, b.Group(AInt(bps.params.group)),
-                            b.Binding(AInt(bps.params.binding)));
-            }
-
-            // Replace the original texture_external binding with a texture_2d<f32> binding.
-            auto cloned_attributes = ctx.Clone(global->attributes);
-
-            // Allow the originating binding to have collisions.
-            if (new_binding_points->allow_collisions) {
-                cloned_attributes.Push(b.Disable(DisabledValidation::kBindingPointCollision));
-            }
-
-            const Expression* cloned_initializer = ctx.Clone(global->initializer);
-
-            auto* replacement = b.Var(
-                syms.plane_0, b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()),
-                cloned_initializer, cloned_attributes);
-            ctx.Replace(global, replacement);
-        }
-
-        // We must update all the texture_external parameters for user declared functions.
-        for (auto* fn : ctx.src->AST().Functions()) {
-            for (const Variable* param : fn->params) {
-                if (auto* sem_var = sem.Get(param)) {
-                    if (!sem_var->Type()->UnwrapRef()->Is<core::type::ExternalTexture>()) {
-                        continue;
-                    }
-                    // If we find a texture_external, we must ensure the ExternalTextureParams
-                    // struct exists.
-                    if (!params_struct_sym.IsValid()) {
-                        createExtTexParamsStructs();
-                    }
-                    // When a texture_external is found, we insert all components the
-                    // texture_external into the parameter list. We must also place the new symbols
-                    // into the transform state so they can be used when transforming function
-                    // calls.
-                    auto& syms = new_binding_symbols[sem_var];
-                    syms.plane_0 = ctx.Clone(param->name->symbol);
-                    syms.plane_1 = b.Symbols().New("ext_tex_plane_1");
-                    syms.params = b.Symbols().New("ext_tex_params");
-                    auto tex2d_f32 = [&] {
-                        return b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32());
-                    };
-                    ctx.Replace(param, b.Param(syms.plane_0, tex2d_f32()));
-                    ctx.InsertAfter(fn->params, param, b.Param(syms.plane_1, tex2d_f32()));
-                    ctx.InsertAfter(fn->params, param,
-                                    b.Param(syms.params, b.ty(params_struct_sym)));
-                }
-            }
-        }
-
-        // Transform the external texture builtin calls into calls to the external texture
-        // functions.
-        ctx.ReplaceAll([&](const CallExpression* expr) -> const Expression* {
-            auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>();
-            auto* builtin = call->Target()->As<sem::BuiltinFn>();
-
-            if (builtin && !builtin->Parameters().IsEmpty() &&
-                builtin->Parameters()[0]->Type()->Is<core::type::ExternalTexture>()) {
-                if (auto* var_user =
-                        sem.GetVal(expr->args[0])->UnwrapLoad()->As<sem::VariableUser>()) {
-                    auto it = new_binding_symbols.find(var_user->Variable());
-                    if (it == new_binding_symbols.end()) {
-                        // If valid new binding locations were not provided earlier, we would have
-                        // been unable to create these symbols. An error message was emitted
-                        // earlier, so just return early to avoid internal compiler errors and
-                        // retain a clean error message.
-                        return nullptr;
-                    }
-                    auto& syms = it->second;
-
-                    switch (builtin->Fn()) {
-                        case wgsl::BuiltinFn::kTextureLoad:
-                            return createTextureLoad(call, syms);
-                        case wgsl::BuiltinFn::kTextureSampleBaseClampToEdge:
-                            return createTextureSampleBaseClampToEdge(expr, syms);
-                        case wgsl::BuiltinFn::kTextureDimensions:
-                            return createTextureDimensions(call, syms);
-                        default:
-                            break;
-                    }
-                }
-            } else if (call->Target()->Is<sem::Function>()) {
-                // The call expression may be to a user-defined function that contains a
-                // texture_external parameter. These need to be expanded out to multiple plane
-                // textures and the texture parameters structure.
-                for (auto* arg : expr->args) {
-                    if (auto* var_user = sem.GetVal(arg)->UnwrapLoad()->As<sem::VariableUser>()) {
-                        // Check if a parameter is a texture_external by trying to find
-                        // it in the transform state.
-                        auto it = new_binding_symbols.find(var_user->Variable());
-                        if (it != new_binding_symbols.end()) {
-                            auto& syms = it->second;
-                            // When we find a texture_external, we must unpack it into its
-                            // components.
-                            ctx.Replace(arg, b.Expr(syms.plane_0));
-                            ctx.InsertAfter(expr->args, arg, b.Expr(syms.plane_1));
-                            ctx.InsertAfter(expr->args, arg, b.Expr(syms.params));
-                        }
-                    }
-                }
-            }
-
-            return nullptr;
-        });
-    }
-
-    /// Creates the parameter structs associated with the transform.
-    void createExtTexParamsStructs() {
-        // Create GammaTransferParams struct.
-        tint::Vector gamma_transfer_member_list{
-            b.Member("G", b.ty.f32()), b.Member("A", b.ty.f32()),      b.Member("B", b.ty.f32()),
-            b.Member("C", b.ty.f32()), b.Member("D", b.ty.f32()),      b.Member("E", b.ty.f32()),
-            b.Member("F", b.ty.f32()), b.Member("padding", b.ty.u32())};
-
-        gamma_transfer_struct_sym = b.Symbols().New("GammaTransferParams");
-
-        b.Structure(gamma_transfer_struct_sym, gamma_transfer_member_list);
-
-        // Create ExternalTextureParams struct.
-        tint::Vector ext_tex_params_member_list{
-            b.Member("numPlanes", b.ty.u32()),
-            b.Member("doYuvToRgbConversionOnly", b.ty.u32()),
-            b.Member("yuvToRgbConversionMatrix", b.ty.mat3x4<f32>()),
-            b.Member("gammaDecodeParams", b.ty("GammaTransferParams")),
-            b.Member("gammaEncodeParams", b.ty("GammaTransferParams")),
-            b.Member("gamutConversionMatrix", b.ty.mat3x3<f32>()),
-            b.Member("sampleTransform", b.ty.mat3x2<f32>()),
-            b.Member("loadTransform", b.ty.mat3x2<f32>()),
-            b.Member("samplePlane0RectMin", b.ty.vec2<f32>()),
-            b.Member("samplePlane0RectMax", b.ty.vec2<f32>()),
-            b.Member("samplePlane1RectMin", b.ty.vec2<f32>()),
-            b.Member("samplePlane1RectMax", b.ty.vec2<f32>()),
-            b.Member("apparentSize", b.ty.vec2<u32>()),
-            b.Member("plane1CoordFactor", b.ty.vec2<f32>())};
-
-        params_struct_sym = b.Symbols().New("ExternalTextureParams");
-
-        b.Structure(params_struct_sym, ext_tex_params_member_list);
-    }
-
-    /// Creates the gammaCorrection function if needed and returns a call
-    /// expression to it.
-    void createGammaCorrectionFn() {
-        gamma_correction_sym = b.Symbols().New("gammaCorrection");
-
-        b.Func(gamma_correction_sym,
-               tint::Vector{
-                   b.Param("v", b.ty.vec3<f32>()),
-                   b.Param("params", b.ty(gamma_transfer_struct_sym)),
-               },
-               b.ty.vec3<f32>(),
-               tint::Vector{
-                   // let cond = abs(v) < vec3(params.D);
-                   b.Decl(b.Let("cond",
-                                b.LessThan(b.Call("abs", "v"),
-                                           b.Call<vec3<f32>>(b.MemberAccessor("params", "D"))))),
-                   // let t = sign(v) * ((params.C * abs(v)) + params.F);
-                   b.Decl(b.Let(
-                       "t", b.Mul(b.Call("sign", "v"),
-                                  b.Add(b.Mul(b.MemberAccessor("params", "C"), b.Call("abs", "v")),
-                                        b.MemberAccessor("params", "F"))))),
-                   // let f = (sign(v) * pow(((params.A * abs(v)) + params.B),
-                   // vec3(params.G))) + params.E;
-                   b.Decl(b.Let(
-                       "f", b.Mul(b.Call("sign", "v"),
-                                  b.Add(b.Call("pow",
-                                               b.Add(b.Mul(b.MemberAccessor("params", "A"),
-                                                           b.Call("abs", "v")),
-                                                     b.MemberAccessor("params", "B")),
-                                               b.Call<vec3<f32>>(b.MemberAccessor("params", "G"))),
-                                        b.MemberAccessor("params", "E"))))),
-                   // return select(f, t, cond);
-                   b.Return(b.Call("select", "f", "t", "cond")),
-               });
-    }
-
-    /// Constructs a StatementList containing all the statements making up the body of the texture
-    /// builtin function.
-    /// @param call_type determines which function body to generate
-    /// @returns a statement list that makes of the body of the chosen function
-    auto buildTextureBuiltinBody(wgsl::BuiltinFn call_type) {
-        tint::Vector<const Statement*, 16> stmts;
-        const BlockStatement* single_plane_block = nullptr;
-        const BlockStatement* multi_plane_block = nullptr;
-        switch (call_type) {
-            case wgsl::BuiltinFn::kTextureSampleBaseClampToEdge:
-                stmts.Push(b.Decl(
-                    b.Let("modifiedCoords", b.Mul(b.MemberAccessor("params", "sampleTransform"),
-                                                  b.Call<vec3<f32>>("coord", 1_a)))));
-
-                stmts.Push(b.Decl(b.Let(
-                    "plane0_clamped", b.Call("clamp", "modifiedCoords",
-                                             b.MemberAccessor("params", "samplePlane0RectMin"),
-                                             b.MemberAccessor("params", "samplePlane0RectMax")))));
-
-                // var color: vec4<f32>;
-                stmts.Push(b.Decl(b.Var("color", b.ty.vec4(b.ty.f32()))));
-
-                single_plane_block = b.Block(
-                    b.Assign("color", b.MemberAccessor(b.Call("textureSampleLevel", "plane0", "smp",
-                                                              "plane0_clamped", 0_a),
-                                                       "rgba")));
-
-                multi_plane_block = b.Block(
-                    b.Decl(b.Let("plane1_clamped",
-                                 b.Call("clamp", "modifiedCoords",
-                                        b.MemberAccessor("params", "samplePlane1RectMin"),
-                                        b.MemberAccessor("params", "samplePlane1RectMax")))),
-
-                    b.Assign("color",
-                             b.Call<vec4<f32>>(
-                                 b.Mul(b.Call<vec4<f32>>(
-                                           b.MemberAccessor(b.Call("textureSampleLevel", "plane0",
-                                                                   "smp", "plane0_clamped", 0_a),
-                                                            "r"),
-                                           b.MemberAccessor(b.Call("textureSampleLevel", "plane1",
-                                                                   "smp", "plane1_clamped", 0_a),
-                                                            "rg"),
-                                           1_a),
-                                       b.MemberAccessor("params", "yuvToRgbConversionMatrix")),
-                                 1_a)));
-                break;
-            case wgsl::BuiltinFn::kTextureLoad:
-                stmts.Push(b.Decl(
-                    b.Let("clampedCoords", b.Call("min", b.Call<vec2<u32>>("coord"),
-                                                  b.MemberAccessor("params", "apparentSize")))));
-                stmts.Push(b.Decl(b.Let(
-                    "plane0_clamped",
-                    b.Call<vec2<u32>>(b.Call(
-                        "round",
-                        b.Mul(b.MemberAccessor("params", "loadTransform"),
-                              b.Call<vec3<f32>>(b.Call<vec2<f32>>("clampedCoords"), 1_a)))))));
-
-                // var color: vec4<f32>;
-                stmts.Push(b.Decl(b.Var("color", b.ty.vec4(b.ty.f32()))));
-
-                single_plane_block = b.Block(b.Assign(
-                    "color", b.MemberAccessor(
-                                 b.Call("textureLoad", "plane0", "plane0_clamped", 0_a), "rgba")));
-
-                multi_plane_block = b.Block(
-                    b.Decl(b.Let(
-                        "plane1_clamped",
-                        b.Call<vec2<u32>>(b.Mul(b.Call<vec2<f32>>("plane0_clamped"),
-                                                b.MemberAccessor("params", "plane1CoordFactor"))))),
-
-                    b.Assign("color",
-                             b.Call<vec4<f32>>(
-                                 b.Mul(b.Call<vec4<f32>>(
-                                           b.MemberAccessor(b.Call("textureLoad", "plane0",
-                                                                   "plane0_clamped", 0_a),
-                                                            "r"),
-                                           b.MemberAccessor(b.Call("textureLoad", "plane1",
-                                                                   "plane1_clamped", 0_a),
-                                                            "rg"),
-                                           1_a),
-                                       b.MemberAccessor("params", "yuvToRgbConversionMatrix")),
-                                 1_a)));
-                break;
-            default:
-                TINT_ICE() << "unhandled builtin: " << call_type;
-        }
-
-        // if ((params.numPlanes == 1u))
-        stmts.Push(b.If(b.Equal(b.MemberAccessor("params", "numPlanes"), b.Expr(1_a)),
-                        single_plane_block, b.Else(multi_plane_block)));
-
-        // if (params.doYuvToRgbConversionOnly == 0u)
-        stmts.Push(b.If(
-            b.Equal(b.MemberAccessor("params", "doYuvToRgbConversionOnly"), b.Expr(0_a)),
-            b.Block(
-                // color = vec4<f32>(gammaConversion(color.rgb, gammaDecodeParams), color.a);
-                b.Assign("color", b.Call<vec4<f32>>(
-                                      b.Call("gammaCorrection", b.MemberAccessor("color", "rgb"),
-                                             b.MemberAccessor("params", "gammaDecodeParams")),
-                                      b.MemberAccessor("color", "a"))),
-                // color = vec4<f32>(params.gamutConversionMatrix * color.rgb), color.a);
-                b.Assign("color", b.Call<vec4<f32>>(
-                                      b.Mul(b.MemberAccessor("params", "gamutConversionMatrix"),
-                                            b.MemberAccessor("color", "rgb")),
-                                      b.MemberAccessor("color", "a"))),
-                // color = vec4<f32>(gammaConversion(color.rgb, gammaEncodeParams), color.a);
-                b.Assign("color", b.Call<vec4<f32>>(
-                                      b.Call("gammaCorrection", b.MemberAccessor("color", "rgb"),
-                                             b.MemberAccessor("params", "gammaEncodeParams")),
-                                      b.MemberAccessor("color", "a"))))));
-
-        // return color;
-        stmts.Push(b.Return("color"));
-
-        return stmts;
-    }
-
-    /// Creates the textureSampleExternal function if needed and returns a call expression to it.
-    /// @param expr the call expression being transformed
-    /// @param syms the expanded symbols to be used in the new call
-    /// @returns a call expression to textureSampleExternal
-    const CallExpression* createTextureSampleBaseClampToEdge(const CallExpression* expr,
-                                                             NewBindingSymbols syms) {
-        const Expression* plane_0_binding_param = ctx.Clone(expr->args[0]);
-
-        if (DAWN_UNLIKELY(expr->args.Length() != 3)) {
-            TINT_ICE() << "expected textureSampleBaseClampToEdge call with a "
-                          "texture_external to have 3 parameters, found "
-                       << expr->args.Length() << " parameters";
-        }
-
-        // TextureSampleExternal calls the gammaCorrection function, so ensure it
-        // exists.
-        if (!gamma_correction_sym.IsValid()) {
-            createGammaCorrectionFn();
-        }
-
-        if (!texture_sample_external_sym.IsValid()) {
-            texture_sample_external_sym = b.Symbols().New("textureSampleExternal");
-
-            // Emit the textureSampleExternal function.
-            b.Func(texture_sample_external_sym,
-                   tint::Vector{
-                       b.Param("plane0",
-                               b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
-                       b.Param("plane1",
-                               b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
-                       b.Param("smp", b.ty.sampler(core::type::SamplerKind::kSampler)),
-                       b.Param("coord", b.ty.vec2(b.ty.f32())),
-                       b.Param("params", b.ty(params_struct_sym)),
-                   },
-                   b.ty.vec4(b.ty.f32()),
-                   buildTextureBuiltinBody(wgsl::BuiltinFn::kTextureSampleBaseClampToEdge));
-        }
-
-        return b.Call(texture_sample_external_sym, tint::Vector{
-                                                       plane_0_binding_param,
-                                                       b.Expr(syms.plane_1),
-                                                       ctx.Clone(expr->args[1]),
-                                                       ctx.Clone(expr->args[2]),
-                                                       b.Expr(syms.params),
-                                                   });
-    }
-
-    /// Creates the textureLoadExternal function if needed and returns a call expression to it.
-    /// @param call the call expression being transformed
-    /// @param syms the expanded symbols to be used in the new call
-    /// @returns a call expression to textureLoadExternal
-    const CallExpression* createTextureLoad(const sem::Call* call, NewBindingSymbols syms) {
-        if (DAWN_UNLIKELY(call->Arguments().Length() != 2)) {
-            TINT_ICE()
-                << "expected textureLoad call with a texture_external to have 2 arguments, found "
-                << call->Arguments().Length() << " arguments";
-        }
-
-        auto& args = call->Arguments();
-
-        // TextureLoadExternal calls the gammaCorrection function, so ensure it exists.
-        if (!gamma_correction_sym.IsValid()) {
-            createGammaCorrectionFn();
-        }
-
-        auto texture_load_external_sym = texture_load_external_fns.GetOrAdd(call->Target(), [&] {
-            auto& sig = call->Target()->Signature();
-            auto* coord_ty = sig.Parameter(core::ParameterUsage::kCoords)->Type();
-
-            auto name = b.Symbols().New("textureLoadExternal");
-
-            // Emit the textureLoadExternal() function.
-            b.Func(name,
-                   tint::Vector{
-                       b.Param("plane0",
-                               b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
-                       b.Param("plane1",
-                               b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())),
-                       b.Param("coord", CreateASTTypeFor(ctx, coord_ty)),
-                       b.Param("params", b.ty(params_struct_sym)),
-                   },
-                   b.ty.vec4(b.ty.f32()),  //
-                   buildTextureBuiltinBody(wgsl::BuiltinFn::kTextureLoad));
-
-            return name;
-        });
-
-        auto plane_0_binding_arg = ctx.Clone(args[0]->Declaration());
-
-        return b.Call(texture_load_external_sym, plane_0_binding_arg, syms.plane_1,
-                      ctx.Clone(args[1]->Declaration()), syms.params);
-    }
-
-    /// Returns the expression used to replace a textureDimensions call.
-    /// @param call the call expression being transformed
-    /// @param syms the expanded symbols to be used in the new call
-    /// @returns a load of params.apparentSize
-    const Expression* createTextureDimensions(const sem::Call* call, NewBindingSymbols syms) {
-        if (DAWN_UNLIKELY(call->Arguments().Length() != 1)) {
-            TINT_ICE() << "expected textureDimensions call with a texture_external to have 1 "
-                          "arguments, found "
-                       << call->Arguments().Length() << " arguments";
-        }
-        return b.Add(b.MemberAccessor(syms.params, "apparentSize"), b.Call<vec2<u32>>(1_a));
-    }
-};
-
-MultiplanarExternalTexture::NewBindingPoints::NewBindingPoints() = default;
-MultiplanarExternalTexture::NewBindingPoints::NewBindingPoints(
-    tint::transform::multiplanar::BindingsMap inputBindingsMap,
-    bool may_collide)
-    : bindings_map(std::move(inputBindingsMap)), allow_collisions(may_collide) {}
-
-MultiplanarExternalTexture::NewBindingPoints::~NewBindingPoints() = default;
-
-MultiplanarExternalTexture::MultiplanarExternalTexture() = default;
-MultiplanarExternalTexture::~MultiplanarExternalTexture() = default;
-
-// Within this transform, an instance of a texture_external binding is unpacked into two
-// texture_2d<f32> bindings representing two possible planes of a single texture and a uniform
-// buffer binding representing a struct of parameters. Calls to texture builtins that contain a
-// texture_external parameter will be transformed into a newly generated version of the function,
-// which can perform the desired operation on a single RGBA plane or on separate Y and UV planes.
-Transform::ApplyResult MultiplanarExternalTexture::Apply(const Program& src,
-                                                         const DataMap& inputs,
-                                                         DataMap&) const {
-    auto* new_binding_points = inputs.Get<NewBindingPoints>();
-
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-    if (!new_binding_points) {
-        b.Diagnostics().AddError(Source{})
-            << "missing new binding point data for " << TypeInfo().name;
-        return resolver::Resolve(b);
-    }
-
-    State state(ctx, new_binding_points);
-
-    state.Process();
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h
deleted file mode 100644
index 9ae282b..0000000
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h
+++ /dev/null
@@ -1,99 +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_WGSL_AST_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
-
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/core/builtin_fn.h"
-#include "src/tint/lang/core/ir/transform/multiplanar_options.h"
-#include "src/tint/lang/wgsl/ast/struct_member.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Within the MultiplanarExternalTexture transform, each instance of a
-/// texture_external binding is unpacked into two texture_2d<f32> bindings
-/// representing two possible planes of a texture and a uniform buffer binding
-/// representing a struct of parameters. Calls to textureLoad or
-/// textureSampleLevel that contain a texture_external parameter will be
-/// transformed into a newly generated version of the function, which can
-/// perform the desired operation on a single RGBA plane or on separate Y and UV
-/// planes, and do colorspace conversions including yuv->rgb conversion, gamma
-/// decoding, gamut conversion, and gamma encoding steps. Specifically
-// for BT.709 to SRGB conversion, it takes the fast path only doing the yuv->rgb
-// step and skipping all other steps.
-class MultiplanarExternalTexture final : public Castable<MultiplanarExternalTexture, Transform> {
-  public:
-    /// NewBindingPoints is consumed by the MultiplanarExternalTexture transform.
-    /// Data holds information about location of each texture_external binding and
-    /// which binding slots it should expand into.
-    struct NewBindingPoints final : public Castable<NewBindingPoints, Data> {
-        /// Constructor
-        NewBindingPoints();
-
-        /// Constructor
-        /// @param bm a map to the new binding slots to use.
-        /// @param may_collide if true, then validation will be disabled for binding point
-        /// collisions generated by this transform
-        explicit NewBindingPoints(tint::transform::multiplanar::BindingsMap bm,
-                                  bool may_collide = false);
-
-        /// Destructor
-        ~NewBindingPoints() override;
-
-        /// A map of new binding points to use.
-        tint::transform::multiplanar::BindingsMap bindings_map;
-
-        /// If true, then validation will be disabled for binding point collisions generated by this
-        /// transform.
-        bool allow_collisions = false;
-
-        /// Reflection for this struct
-        TINT_REFLECT(NewBindingPoints, bindings_map, allow_collisions);
-    };
-
-    /// Constructor
-    MultiplanarExternalTexture();
-    /// Destructor
-    ~MultiplanarExternalTexture() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
diff --git a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc b/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
deleted file mode 100644
index 8214c0a..0000000
--- a/src/tint/lang/wgsl/ast/transform/multiplanar_external_texture_test.cc
+++ /dev/null
@@ -1,2014 +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/wgsl/ast/transform/multiplanar_external_texture.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using MultiplanarExternalTextureTest = TransformTest;
-
-TEST_F(MultiplanarExternalTextureTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-
-    EXPECT_FALSE(ShouldRun<MultiplanarExternalTexture>(src, data));
-}
-
-TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureAlias) {
-    auto* src = R"(
-alias ET = texture_external;
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-
-    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
-}
-TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureGlobal) {
-    auto* src = R"(
-@group(0) @binding(0) var ext_tex : texture_external;
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-
-    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
-}
-
-TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureParam) {
-    auto* src = R"(
-fn f(ext_tex : texture_external) {}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-
-    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
-}
-
-// Running the transform without passing in data for the new bindings should result in an error.
-TEST_F(MultiplanarExternalTextureTest, ErrorNoPassedData_SampleBaseClampToEdge) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleBaseClampToEdge(ext_tex, s, coord.xy);
-}
-)";
-    auto* expect =
-        "error: missing new binding point data for "
-        "tint::ast::transform::MultiplanarExternalTexture";
-
-    auto got = Run<MultiplanarExternalTexture>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Running the transform with incorrect binding data should result in an error.
-TEST_F(MultiplanarExternalTextureTest, ErrorIncorrectBindingPont_SampleBaseClampToEdge) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleBaseClampToEdge(ext_tex, s, coord.xy);
-}
-)";
-
-    auto* expect = R"(error: missing new binding points for texture_external at binding {0,1})";
-
-    DataMap data;
-    // This bindings map specifies 0,0 as the location of the texture_external,
-    // which is incorrect.
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a textureDimensions call.
-TEST_F(MultiplanarExternalTextureTest, Dimensions) {
-    auto* src = R"(
-@group(0) @binding(0) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  var dim : vec2<u32>;
-  dim = textureDimensions(ext_tex);
-  return vec4<f32>(0.0, 0.0, 0.0, 0.0);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  var dim : vec2<u32>;
-  dim = (ext_tex_params.apparentSize + vec2<u32>(1));
-  return vec4<f32>(0.0, 0.0, 0.0, 0.0);
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(MultiplanarExternalTextureTest, Collisions) {
-    auto* src = R"(
-@group(0) @binding(0) var myTexture: texture_external;
-
-@fragment
-fn fragmentMain() -> @location(0) vec4f {
-  let result = textureLoad(myTexture, vec2u(1, 1));
-  return vec4f(1);
-})";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@internal(disable_validation__binding_point_collision) @group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@internal(disable_validation__binding_point_collision) @group(0) @binding(0) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var myTexture : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<u32>, params : ExternalTextureParams) -> vec4<f32> {
-  let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
-  let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
-    color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-@fragment
-fn fragmentMain() -> @location(0) vec4f {
-  let result = textureLoadExternal(myTexture, ext_tex_plane_1, vec2u(1, 1), ext_tex_params);
-  return vec4f(1);
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 0}}}},
-        /* allow collisions */ true);
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a textureDimensions call.
-TEST_F(MultiplanarExternalTextureTest, Dimensions_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  var dim : vec2<u32>;
-  dim = textureDimensions(ext_tex);
-  return vec4<f32>(0.0, 0.0, 0.0, 0.0);
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  var dim : vec2<u32>;
-  dim = (ext_tex_params.apparentSize + vec2<u32>(1));
-  return vec4<f32>(0.0, 0.0, 0.0, 0.0);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that the transform works with a textureSampleBaseClampToEdge call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleBaseClampToEdge) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleBaseClampToEdge(ext_tex, s, coord.xy);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params);
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that the transform works with a textureSampleBaseClampToEdge call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleBaseClampToEdge_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleBaseClampToEdge(ext_tex, s, coord.xy);
-}
-
-@group(0) @binding(1) var ext_tex : texture_external;
-@group(0) @binding(0) var s : sampler;
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params);
-}
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(0) var s : sampler;
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureLoad) {
-    auto* src = R"(
-@group(0) @binding(0) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  var val_signed = textureLoad(ext_tex, vec2<i32>(1));
-  var val_unsigned = textureLoad(ext_tex, vec2<u32>(1));
-  return val_signed + val_unsigned;
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
-  let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
-  let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
-    color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn textureLoadExternal_1(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<u32>, params : ExternalTextureParams) -> vec4<f32> {
-  let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
-  let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
-    color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  var val_signed = textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1), ext_tex_params);
-  var val_unsigned = textureLoadExternal_1(ext_tex, ext_tex_plane_1, vec2<u32>(1), ext_tex_params);
-  return (val_signed + val_unsigned);
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, BasicTextureLoad_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  var val_signed = textureLoad(ext_tex, vec2<i32>(1));
-  var val_unsigned = textureLoad(ext_tex, vec2<u32>(1));
-  return val_signed + val_unsigned;
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(1) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(2) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
-  let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
-  let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
-    color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn textureLoadExternal_1(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<u32>, params : ExternalTextureParams) -> vec4<f32> {
-  let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
-  let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
-    color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  var val_signed = textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1), ext_tex_params);
-  var val_unsigned = textureLoadExternal_1(ext_tex, ext_tex_plane_1, vec2<u32>(1), ext_tex_params);
-  return (val_signed + val_unsigned);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with both a textureSampleBaseClampToEdge and textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, TextureSampleBaseClampToEdgeAndTextureLoad) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleBaseClampToEdge(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2<i32>(1, 1));
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
-  let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
-  let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
-    color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params));
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with both a textureSampleBaseClampToEdge and textureLoad call.
-TEST_F(MultiplanarExternalTextureTest, TextureSampleBaseClampToEdgeAndTextureLoad_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleBaseClampToEdge(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2<i32>(1, 1));
-}
-
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
-  let clampedCoords = min(vec2<u32>(coord), params.apparentSize);
-  let plane0_clamped = vec2<u32>(round((params.loadTransform * vec3<f32>(vec2<f32>(clampedCoords), 1))));
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureLoad(plane0, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = vec2<u32>((vec2<f32>(plane0_clamped) * params.plane1CoordFactor));
-    color = vec4<f32>((vec4<f32>(textureLoad(plane0, plane0_clamped, 0).r, textureLoad(plane1, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params));
-}
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with many instances of texture_external.
-TEST_F(MultiplanarExternalTextureTest, ManyTextureSampleBaseClampToEdge) {
-    auto* src = R"(
-@group(0) @binding(0) var s : sampler;
-@group(0) @binding(1) var ext_tex : texture_external;
-@group(0) @binding(2) var ext_tex_1 : texture_external;
-@group(0) @binding(3) var ext_tex_2 : texture_external;
-@group(1) @binding(0) var ext_tex_3 : texture_external;
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return textureSampleBaseClampToEdge(ext_tex, s, coord.xy) +
-         textureSampleBaseClampToEdge(ext_tex_1, s, coord.xy) +
-         textureSampleBaseClampToEdge(ext_tex_2, s, coord.xy) +
-         textureSampleBaseClampToEdge(ext_tex_3, s, coord.xy);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(4) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(5) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(6) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(7) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
-@group(0) @binding(8) var ext_tex_plane_1_2 : texture_2d<f32>;
-
-@group(0) @binding(9) var<uniform> ext_tex_params_2 : ExternalTextureParams;
-
-@group(1) @binding(1) var ext_tex_plane_1_3 : texture_2d<f32>;
-
-@group(1) @binding(2) var<uniform> ext_tex_params_3 : ExternalTextureParams;
-
-@group(0) @binding(0) var s : sampler;
-
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(2) var ext_tex_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var ext_tex_2 : texture_2d<f32>;
-
-@group(1) @binding(0) var ext_tex_3 : texture_2d<f32>;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-@fragment
-fn main(@builtin(position) coord : vec4<f32>) -> @location(0) vec4<f32> {
-  return (((textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureSampleExternal(ext_tex_1, ext_tex_plane_1_1, s, coord.xy, ext_tex_params_1)) + textureSampleExternal(ext_tex_2, ext_tex_plane_1_2, s, coord.xy, ext_tex_params_2)) + textureSampleExternal(ext_tex_3, ext_tex_plane_1_3, s, coord.xy, ext_tex_params_3));
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 1}, {{0, 4}, {0, 5}}},
-            {{0, 2}, {{0, 6}, {0, 7}}},
-            {{0, 3}, {{0, 8}, {0, 9}}},
-            {{1, 0}, {{1, 1}, {1, 2}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed as a function parameter produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParam) {
-    auto* src = R"(
-fn f(t : texture_external, s : sampler) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(ext_tex, smp);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 2}, {0, 3}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed as a function parameter produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParam_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main() {
-  f(ext_tex, smp);
-}
-
-fn f(t : texture_external, s : sampler) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@fragment
-fn main() {
-  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 2}, {0, 3}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed as a parameter not in the first
-// position produces the correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsSecondParam) {
-    auto* src = R"(
-fn f(s : sampler, t : texture_external) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(smp, ext_tex);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn f(s : sampler, t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(smp, ext_tex, ext_tex_plane_1, ext_tex_params);
-}
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 2}, {0, 3}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that multiple texture_external params passed to a function produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamMultiple) {
-    auto* src = R"(
-fn f(t : texture_external, s : sampler, t2 : texture_external) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-  _ = textureSampleBaseClampToEdge(t2, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-@group(0) @binding(2) var ext_tex2 : texture_external;
-
-@fragment
-fn main() {
-  f(ext_tex, smp, ext_tex2);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(3) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(4) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(5) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(6) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler, t2 : texture_2d<f32>, ext_tex_plane_1_3 : texture_2d<f32>, ext_tex_params_3 : ExternalTextureParams) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_2, s, vec2<f32>(1.0, 2.0), ext_tex_params_2);
-  _ = textureSampleExternal(t2, ext_tex_plane_1_3, s, vec2<f32>(1.0, 2.0), ext_tex_params_3);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@group(0) @binding(2) var ext_tex2 : texture_2d<f32>;
-
-@fragment
-fn main() {
-  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp, ext_tex2, ext_tex_plane_1_1, ext_tex_params_1);
-}
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 3}, {0, 4}}},
-            {{0, 2}, {{0, 5}, {0, 6}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that multiple texture_external params passed to a function produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamMultiple_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main() {
-  f(ext_tex, smp, ext_tex2);
-}
-
-fn f(t : texture_external, s : sampler, t2 : texture_external) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-  _ = textureSampleBaseClampToEdge(t2, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-@group(0) @binding(2) var ext_tex2 : texture_external;
-
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(3) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(4) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@group(0) @binding(5) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(6) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
-@fragment
-fn main() {
-  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp, ext_tex2, ext_tex_plane_1_1, ext_tex_params_1);
-}
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler, t2 : texture_2d<f32>, ext_tex_plane_1_3 : texture_2d<f32>, ext_tex_params_3 : ExternalTextureParams) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_2, s, vec2<f32>(1.0, 2.0), ext_tex_params_2);
-  _ = textureSampleExternal(t2, ext_tex_plane_1_3, s, vec2<f32>(1.0, 2.0), ext_tex_params_3);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@group(0) @binding(2) var ext_tex2 : texture_2d<f32>;
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 3}, {0, 4}}},
-            {{0, 2}, {{0, 5}, {0, 6}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed to as a parameter to multiple
-// functions produces the correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamNested) {
-    auto* src = R"(
-fn nested(t : texture_external, s : sampler) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-fn f(t : texture_external, s : sampler) {
-  nested(t, s);
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(ext_tex, smp);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn nested(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler) {
-  nested(t, ext_tex_plane_1_2, ext_tex_params_2, s);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 2}, {0, 3}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the texture_external passed to as a parameter to multiple functions produces the
-// correct output.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamNested_OutOfOrder) {
-    auto* src = R"(
-fn nested(t : texture_external, s : sampler) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-fn f(t : texture_external, s : sampler) {
-  nested(t, s);
-}
-
-@group(0) @binding(0) var ext_tex : texture_external;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(ext_tex, smp);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn nested(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler) {
-  nested(t, ext_tex_plane_1_2, ext_tex_params_2, s);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 2}, {0, 3}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform works with a function using an external texture,
-// even if there's no external texture declared at module scope.
-TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParamWithoutGlobalDecl) {
-    auto* src = R"(
-fn f(ext_tex : texture_external) -> vec2<u32> {
-  return textureDimensions(ext_tex);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-fn f(ext_tex : texture_2d<f32>, ext_tex_plane_1 : texture_2d<f32>, ext_tex_params : ExternalTextureParams) -> vec2<u32> {
-  return (ext_tex_params.apparentSize + vec2<u32>(1));
-}
-)";
-
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform handles aliases to external textures
-TEST_F(MultiplanarExternalTextureTest, ExternalTextureAlias) {
-    auto* src = R"(
-alias ET = texture_external;
-
-fn f(t : ET, s : sampler) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : ET;
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(ext_tex, smp);
-}
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-alias ET = texture_external;
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-@fragment
-fn main() {
-  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 2}, {0, 3}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Tests that the transform handles aliases to external textures
-TEST_F(MultiplanarExternalTextureTest, ExternalTextureAlias_OutOfOrder) {
-    auto* src = R"(
-@fragment
-fn main() {
-  f(ext_tex, smp);
-}
-
-fn f(t : ET, s : sampler) {
-  _ = textureSampleBaseClampToEdge(t, s, vec2<f32>(1.0, 2.0));
-}
-
-@group(0) @binding(0) var ext_tex : ET;
-@group(0) @binding(1) var smp : sampler;
-
-alias ET = texture_external;
-)";
-
-    auto* expect = R"(
-struct GammaTransferParams {
-  G : f32,
-  A : f32,
-  B : f32,
-  C : f32,
-  D : f32,
-  E : f32,
-  F : f32,
-  padding : u32,
-}
-
-struct ExternalTextureParams {
-  numPlanes : u32,
-  doYuvToRgbConversionOnly : u32,
-  yuvToRgbConversionMatrix : mat3x4<f32>,
-  gammaDecodeParams : GammaTransferParams,
-  gammaEncodeParams : GammaTransferParams,
-  gamutConversionMatrix : mat3x3<f32>,
-  sampleTransform : mat3x2<f32>,
-  loadTransform : mat3x2<f32>,
-  samplePlane0RectMin : vec2<f32>,
-  samplePlane0RectMax : vec2<f32>,
-  samplePlane1RectMin : vec2<f32>,
-  samplePlane1RectMax : vec2<f32>,
-  apparentSize : vec2<u32>,
-  plane1CoordFactor : vec2<f32>,
-}
-
-@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-
-@fragment
-fn main() {
-  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
-}
-
-fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
-  let cond = (abs(v) < vec3<f32>(params.D));
-  let t = (sign(v) * ((params.C * abs(v)) + params.F));
-  let f = (sign(v) * (pow(((params.A * abs(v)) + params.B), vec3<f32>(params.G)) + params.E));
-  return select(f, t, cond);
-}
-
-fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
-  let modifiedCoords = (params.sampleTransform * vec3<f32>(coord, 1));
-  let plane0_clamped = clamp(modifiedCoords, params.samplePlane0RectMin, params.samplePlane0RectMax);
-  var color : vec4<f32>;
-  if ((params.numPlanes == 1)) {
-    color = textureSampleLevel(plane0, smp, plane0_clamped, 0).rgba;
-  } else {
-    let plane1_clamped = clamp(modifiedCoords, params.samplePlane1RectMin, params.samplePlane1RectMax);
-    color = vec4<f32>((vec4<f32>(textureSampleLevel(plane0, smp, plane0_clamped, 0).r, textureSampleLevel(plane1, smp, plane1_clamped, 0).rg, 1) * params.yuvToRgbConversionMatrix), 1);
-  }
-  if ((params.doYuvToRgbConversionOnly == 0)) {
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaDecodeParams), color.a);
-    color = vec4<f32>((params.gamutConversionMatrix * color.rgb), color.a);
-    color = vec4<f32>(gammaCorrection(color.rgb, params.gammaEncodeParams), color.a);
-  }
-  return color;
-}
-
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
-  _ = textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
-}
-
-@group(0) @binding(0) var ext_tex : texture_2d<f32>;
-
-@group(0) @binding(1) var smp : sampler;
-
-alias ET = texture_external;
-)";
-    DataMap data;
-    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
-        tint::transform::multiplanar::BindingsMap{
-            {{0, 0}, {{0, 2}, {0, 3}}},
-        });
-    auto got = Run<MultiplanarExternalTexture>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.cc b/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.cc
deleted file mode 100644
index 46176c1..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.cc
+++ /dev/null
@@ -1,171 +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/wgsl/ast/transform/promote_initializers_to_let.h"
-
-#include "src/tint/lang/core/type/struct.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/ast/traverse_expressions.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/statement.h"
-#include "src/tint/lang/wgsl/sem/value_constructor.h"
-#include "src/tint/utils/containers/hashset.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::PromoteInitializersToLet);
-
-namespace tint::ast::transform {
-
-PromoteInitializersToLet::PromoteInitializersToLet() = default;
-
-PromoteInitializersToLet::~PromoteInitializersToLet() = default;
-
-Transform::ApplyResult PromoteInitializersToLet::Apply(const Program& src,
-                                                       const DataMap&,
-                                                       DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    // Returns true if the expression should be hoisted to a new let statement before the
-    // expression's statement.
-    auto should_hoist = [&](const sem::ValueExpression* expr) {
-        if (!expr->Type()->IsAnyOf<core::type::Array, core::type::Struct>()) {
-            // We only care about array and struct initializers
-            return false;
-        }
-
-        if (expr->Type()->IsAbstract()) {
-            // Do not hoist expressions that are not materialized, as doing so would cause
-            // premature materialization.
-            return false;
-        }
-
-        // Check whether the expression is an array or structure constructor
-        {
-            // Follow const-chains
-            auto* root_expr = expr;
-            if (expr->Stage() == core::EvaluationStage::kConstant) {
-                while (auto* user = root_expr->UnwrapMaterialize()->As<sem::VariableUser>()) {
-                    root_expr = user->Variable()->Initializer();
-                }
-            }
-
-            auto* ctor = root_expr->UnwrapMaterialize()->As<sem::Call>();
-            if (!ctor || !ctor->Target()->Is<sem::ValueConstructor>()) {
-                // Root expression is not a value constructor. Not interested in this.
-                return false;
-            }
-        }
-
-        if (auto* src_var_decl = expr->Stmt()->Declaration()->As<VariableDeclStatement>()) {
-            if (src_var_decl->variable->initializer == expr->Declaration()) {
-                // This statement is just a variable declaration with the initializer as the
-                // initializer value. This is what we're attempting to transform to, and so
-                // ignore.
-                return false;
-            }
-        }
-
-        return true;
-    };
-
-    // A list of expressions that should be hoisted.
-    tint::Vector<const sem::ValueExpression*, 32> to_hoist;
-    // A set of expressions that are constant, which _may_ need to be hoisted.
-    Hashset<const Expression*, 32> const_chains;
-
-    // Walk the AST nodes. This order guarantees that leaf-expressions are visited first.
-    for (auto* node : src.ASTNodes().Objects()) {
-        if (auto* sem = src.Sem().GetVal(node)) {
-            auto* stmt = sem->Stmt();
-            if (!stmt) {
-                // Expression is outside of a statement. This usually means the expression is part
-                // of a global (module-scope) constant declaration. These must be constexpr, and so
-                // cannot contain the type of expressions that must be sanitized.
-                continue;
-            }
-
-            if (sem->Stage() == core::EvaluationStage::kConstant ||
-                sem->Stage() == core::EvaluationStage::kNotEvaluated) {
-                // Expression is constant or not evaluated. We only need to hoist expressions if
-                // they're the outermost constant expression in a chain. Remove the immediate child
-                // nodes of the expression from const_chains, and add this expression to the
-                // const_chains. As we visit leaf-expressions first, this means the content of
-                // const_chains only contains the outer-most constant expressions.
-                auto* expr = sem->Declaration();
-                bool ok = TraverseExpressions(expr, [&](const Expression* child) {
-                    const_chains.Remove(child);
-                    return child == expr ? TraverseAction::Descend : TraverseAction::Skip;
-                });
-                if (!ok) {
-                    return resolver::Resolve(b);
-                }
-                if (sem->Stage() == core::EvaluationStage::kConstant) {
-                    const_chains.Add(expr);
-                }
-            } else if (should_hoist(sem)) {
-                to_hoist.Push(sem);
-            }
-        }
-    }
-
-    // After walking the full AST, const_chains only contains the outer-most constant expressions.
-    // Check if any of these need hoisting, and append those to to_hoist.
-    for (auto& expr : const_chains) {
-        if (auto* sem = src.Sem().GetVal(expr.Value()); should_hoist(sem)) {
-            to_hoist.Push(sem);
-        }
-    }
-
-    if (to_hoist.IsEmpty()) {
-        // Nothing to do. Skip.
-        return SkipTransform;
-    }
-
-    // The order of to_hoist is currently undefined. Sort by AST node id, which will make this
-    // deterministic.
-    to_hoist.Sort([&](auto* expr_a, auto* expr_b) {
-        return expr_a->Declaration()->node_id < expr_b->Declaration()->node_id;
-    });
-
-    // Hoist all the expressions in to_hoist to a constant variable, declared just before the
-    // statement of usage.
-    HoistToDeclBefore hoist_to_decl_before(ctx);
-    for (auto* expr : to_hoist) {
-        if (!hoist_to_decl_before.Add(expr, expr->Declaration(),
-                                      HoistToDeclBefore::VariableKind::kLet)) {
-            return resolver::Resolve(b);
-        }
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h b/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h
deleted file mode 100644
index 4ce1988..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h
+++ /dev/null
@@ -1,57 +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_WGSL_AST_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// A transform that hoists array and structure initializers, and identifiers resolving to a
-/// 'const' array to a 'let' variable, declared just before the statement of usage.
-/// This transform is used by backends that do not support expressions that operate on an immediate
-/// array or structure. For example, the following is not immediately expressible for HLSL:
-///   `array<i32, 2>(1, 2)[0]`
-/// @see crbug.com/tint/406
-class PromoteInitializersToLet final : public Castable<PromoteInitializersToLet, Transform> {
-  public:
-    /// Constructor
-    PromoteInitializersToLet();
-
-    /// Destructor
-    ~PromoteInitializersToLet() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
diff --git a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_test.cc b/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_test.cc
deleted file mode 100644
index ded078e..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_initializers_to_let_test.cc
+++ /dev/null
@@ -1,1410 +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/wgsl/ast/transform/promote_initializers_to_let.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using PromoteInitializersToLetTest = TransformTest;
-
-TEST_F(PromoteInitializersToLetTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = "";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicConstArray) {
-    auto* src = R"(
-fn f() {
-  const f0 = 1.0;
-  const f1 = 2.0;
-  const f2 = 3.0;
-  const f3 = 4.0;
-  var i = array<f32, 4u>(f0, f1, f2, f3)[2];
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicRuntimeArray) {
-    auto* src = R"(
-fn f() {
-  var f0 = 1.0;
-  var f1 = 2.0;
-  var f2 = 3.0;
-  var f3 = 4.0;
-  var i = array<f32, 4u>(f0, f1, f2, f3)[2];
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f0 = 1.0;
-  var f1 = 2.0;
-  var f2 = 3.0;
-  var f3 = 4.0;
-  let tint_symbol : array<f32, 4u> = array<f32, 4u>(f0, f1, f2, f3);
-  var i = tint_symbol[2];
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicConstStruct) {
-    auto* src = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-
-fn f() {
-  var x = S(1, 2.0, vec3<f32>()).b;
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicRuntimeStruct) {
-    auto* src = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-
-fn f() {
-  let runtime_value = 1;
-  var x = S(runtime_value, 2.0, vec3<f32>()).b;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-}
-
-fn f() {
-  let runtime_value = 1;
-  let tint_symbol : S = S(runtime_value, 2.0, vec3<f32>());
-  var x = tint_symbol.b;
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, BasicStruct_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 1;
-  var x = S(runtime_value, 2.0, vec3<f32>()).b;
-}
-
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 1;
-  let tint_symbol : S = S(runtime_value, 2.0, vec3<f32>());
-  var x = tint_symbol.b;
-}
-
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstBasicArray) {
-    auto* src = R"(
-const f0 = 1.0;
-
-const f1 = 2.0;
-
-const C = array<f32, 2u>(f0, f1);
-
-fn f() {
-  var f0 = 100.0;
-  var f1 = 100.0;
-  var i = C[1]; // Not hoisted, as the final const value is not an array
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstBasicArray_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var f0 = 100.0;
-  var f1 = 100.0;
-  var i = C[1];
-}
-
-const C = array<f32, 2u>(f0, f1);
-
-const f0 = 1.0;
-
-const f1 = 2.0;
-)";
-
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayDynamicIndex) {
-    auto* src = R"(
-const TRI_VERTICES = array(
-  vec4(0., 0., 0., 1.),
-  vec4(0., 1., 0., 1.),
-  vec4(1., 1., 0., 1.),
-);
-
-@vertex
-fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
-  // note: TRI_VERTICES requires a materialize before the dynamic index.
-  return TRI_VERTICES[in_vertex_index];
-}
-)";
-
-    auto* expect = R"(
-const TRI_VERTICES = array(vec4(0.0, 0.0, 0.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0));
-
-@vertex
-fn vs_main(@builtin(vertex_index) in_vertex_index : u32) -> @builtin(position) vec4<f32> {
-  let tint_symbol : array<vec4<f32>, 3u> = TRI_VERTICES;
-  return tint_symbol[in_vertex_index];
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstBasicArray) {
-    auto* src = R"(
-fn f() {
-  const f0 = 1.0;
-  const f1 = 2.0;
-  const C = array<f32, 2u>(f0, f1);
-  var i = C[1];  // Not hoisted, as the final const value is not an array
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstBasicArrayRuntimeIndex) {
-    auto* src = R"(
-fn f() {
-  const f0 = 1.0;
-  const f1 = 2.0;
-  const C = array<f32, 2u>(f0, f1);
-  let runtime_value = 1;
-  var i = C[runtime_value];
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  const f0 = 1.0;
-  const f1 = 2.0;
-  const C = array<f32, 2u>(f0, f1);
-  let runtime_value = 1;
-  let tint_symbol : array<f32, 2u> = C;
-  var i = tint_symbol[runtime_value];
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInForLoopInit) {
-    auto* src = R"(
-fn f() {
-  var insert_after = 1;
-  for(var i = array<f32, 4u>(0.0, 1.0, 2.0, 3.0)[insert_after]; ; ) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var insert_after = 1;
-  {
-    let tint_symbol : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-    var i = tint_symbol[insert_after];
-    loop {
-      {
-        break;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopInit) {
-    auto* src = R"(
-fn f() {
-  const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-  let runtime_value = 1;
-  var insert_after = 1;
-  for(var i = arr[runtime_value]; ; ) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-  let runtime_value = 1;
-  var insert_after = 1;
-  {
-    let tint_symbol : array<f32, 4u> = arr;
-    var i = tint_symbol[runtime_value];
-    loop {
-      {
-        break;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopInit) {
-    auto* src = R"(
-const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-
-fn f() {
-  let runtime_value = 1;
-  var insert_after = 1;
-  for(var i = arr[runtime_value]; ; ) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-
-fn f() {
-  let runtime_value = 1;
-  var insert_after = 1;
-  {
-    let tint_symbol : array<f32, 4u> = arr;
-    var i = tint_symbol[runtime_value];
-    loop {
-      {
-        break;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, StructInForLoopInit) {
-    auto* src = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-
-fn get_b_runtime(s : S) -> f32 {
-  return s.b;
-}
-
-fn f() {
-  var insert_after = 1;
-  for(var x = get_b_runtime(S(1, 2.0, vec3<f32>())); ; ) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-}
-
-fn get_b_runtime(s : S) -> f32 {
-  return s.b;
-}
-
-fn f() {
-  var insert_after = 1;
-  {
-    let tint_symbol : S = S(1, 2.0, vec3<f32>());
-    var x = get_b_runtime(tint_symbol);
-    loop {
-      {
-        break;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, StructInForLoopInit_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var insert_after = 1;
-  for(var x = get_b_runtime(S(1, 2.0, vec3<f32>())); ; ) {
-    break;
-  }
-}
-
-fn get_b_runtime(s : S) -> f32 {
-  return s.b;
-}
-
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-)";
-
-    auto* expect = R"(
-fn f() {
-  var insert_after = 1;
-  {
-    let tint_symbol : S = S(1, 2.0, vec3<f32>());
-    var x = get_b_runtime(tint_symbol);
-    loop {
-      {
-        break;
-      }
-    }
-  }
-}
-
-fn get_b_runtime(s : S) -> f32 {
-  return s.b;
-}
-
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInForLoopCond) {
-    auto* src = R"(
-fn f() {
-  var f = 1.0;
-  for(; f == array<f32, 1u>(f)[0]; f = f + 1.0) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f = 1.0;
-  loop {
-    let tint_symbol : array<f32, 1u> = array<f32, 1u>(f);
-    if (!((f == tint_symbol[0]))) {
-      break;
-    }
-    {
-      var marker = 1;
-    }
-
-    continuing {
-      f = (f + 1.0);
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopCond) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 0;
-  const f = 1.0;
-  const arr = array<f32, 1u>(f);
-  for(var i = f; i == arr[runtime_value]; i = i + 1.0) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 0;
-  const f = 1.0;
-  const arr = array<f32, 1u>(f);
-  {
-    var i = f;
-    loop {
-      let tint_symbol : array<f32, 1u> = arr;
-      if (!((i == tint_symbol[runtime_value]))) {
-        break;
-      }
-      {
-        var marker = 1;
-      }
-
-      continuing {
-        i = (i + 1.0);
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopCond) {
-    auto* src = R"(
-const f = 1.0;
-
-const arr = array<f32, 1u>(f);
-
-fn F() {
-  let runtime_value = 0;
-  for(var i = f; i == arr[runtime_value]; i = i + 1.0) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-const f = 1.0;
-
-const arr = array<f32, 1u>(f);
-
-fn F() {
-  let runtime_value = 0;
-  {
-    var i = f;
-    loop {
-      let tint_symbol : array<f32, 1u> = arr;
-      if (!((i == tint_symbol[runtime_value]))) {
-        break;
-      }
-      {
-        var marker = 1;
-      }
-
-      continuing {
-        i = (i + 1.0);
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInForLoopCont) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 0;
-  var f = 0.0;
-  for(; f < 10.0; f = f + array<f32, 1u>(1.0)[runtime_value]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 0;
-  var f = 0.0;
-  loop {
-    if (!((f < 10.0))) {
-      break;
-    }
-    {
-      var marker = 1;
-    }
-
-    continuing {
-      let tint_symbol : array<f32, 1u> = array<f32, 1u>(1.0);
-      f = (f + tint_symbol[runtime_value]);
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopCont) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 0;
-  const arr = array<f32, 1u>(1.0);
-  var f = 0.0;
-  for(; f < 10.0; f = f + arr[runtime_value]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 0;
-  const arr = array<f32, 1u>(1.0);
-  var f = 0.0;
-  loop {
-    if (!((f < 10.0))) {
-      break;
-    }
-    {
-      var marker = 1;
-    }
-
-    continuing {
-      let tint_symbol : array<f32, 1u> = arr;
-      f = (f + tint_symbol[runtime_value]);
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopCont) {
-    auto* src = R"(
-const arr = array<f32, 1u>(1.0);
-
-fn f() {
-  let runtime_value = 0;
-  var f = 0.0;
-  for(; f < 10.0; f = f + arr[runtime_value]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-const arr = array<f32, 1u>(1.0);
-
-fn f() {
-  let runtime_value = 0;
-  var f = 0.0;
-  loop {
-    if (!((f < 10.0))) {
-      break;
-    }
-    {
-      var marker = 1;
-    }
-
-    continuing {
-      let tint_symbol : array<f32, 1u> = arr;
-      f = (f + tint_symbol[runtime_value]);
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInForLoopInitCondCont) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 0;
-  for(var f = array<f32, 1u>(0.0)[runtime_value];
-      f < array<f32, 1u>(1.0)[runtime_value];
-      f = f + array<f32, 1u>(2.0)[runtime_value]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 0;
-  {
-    let tint_symbol : array<f32, 1u> = array<f32, 1u>(0.0);
-    var f = tint_symbol[runtime_value];
-    loop {
-      let tint_symbol_1 : array<f32, 1u> = array<f32, 1u>(1.0);
-      if (!((f < tint_symbol_1[runtime_value]))) {
-        break;
-      }
-      {
-        var marker = 1;
-      }
-
-      continuing {
-        let tint_symbol_2 : array<f32, 1u> = array<f32, 1u>(2.0);
-        f = (f + tint_symbol_2[runtime_value]);
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopInitCondCont) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 0;
-  const arr_a = array<f32, 1u>(0.0);
-  const arr_b = array<f32, 1u>(1.0);
-  const arr_c = array<f32, 1u>(2.0);
-  for(var f = arr_a[runtime_value]; f < arr_b[runtime_value]; f = f + arr_c[runtime_value]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 0;
-  const arr_a = array<f32, 1u>(0.0);
-  const arr_b = array<f32, 1u>(1.0);
-  const arr_c = array<f32, 1u>(2.0);
-  {
-    let tint_symbol : array<f32, 1u> = arr_a;
-    var f = tint_symbol[runtime_value];
-    loop {
-      let tint_symbol_1 : array<f32, 1u> = arr_b;
-      if (!((f < tint_symbol_1[runtime_value]))) {
-        break;
-      }
-      {
-        var marker = 1;
-      }
-
-      continuing {
-        let tint_symbol_2 : array<f32, 1u> = arr_c;
-        f = (f + tint_symbol_2[runtime_value]);
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInElseIf) {
-    auto* src = R"(
-fn f() {
-  var f = 1.0;
-  if (true) {
-    var marker = 0;
-  } else if (f == array<f32, 2u>(f, f)[0]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f = 1.0;
-  if (true) {
-    var marker = 0;
-  } else {
-    let tint_symbol : array<f32, 2u> = array<f32, 2u>(f, f);
-    if ((f == tint_symbol[0])) {
-      var marker = 1;
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInElseIfChain) {
-    auto* src = R"(
-fn f() {
-  var f = 1.0;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else if (f == array<f32, 2u>(f, f)[0]) {
-    var marker = 2;
-  } else if (f == array<f32, 2u>(f, f)[1]) {
-    var marker = 3;
-  } else if (true) {
-    var marker = 4;
-  } else {
-    var marker = 5;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f = 1.0;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else {
-    let tint_symbol : array<f32, 2u> = array<f32, 2u>(f, f);
-    if ((f == tint_symbol[0])) {
-      var marker = 2;
-    } else {
-      let tint_symbol_1 : array<f32, 2u> = array<f32, 2u>(f, f);
-      if ((f == tint_symbol_1[1])) {
-        var marker = 3;
-      } else if (true) {
-        var marker = 4;
-      } else {
-        var marker = 5;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInElseIfChain) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 0;
-  const f = 1.0;
-  const arr = array<f32, 2u>(f, f);
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else if (f == arr[runtime_value]) {
-    var marker = 2;
-  } else if (f == arr[runtime_value + 1]) {
-    var marker = 3;
-  } else if (true) {
-    var marker = 4;
-  } else {
-    var marker = 5;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 0;
-  const f = 1.0;
-  const arr = array<f32, 2u>(f, f);
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else {
-    let tint_symbol : array<f32, 2u> = arr;
-    if ((f == tint_symbol[runtime_value])) {
-      var marker = 2;
-    } else {
-      let tint_symbol_1 : array<f32, 2u> = arr;
-      if ((f == tint_symbol_1[(runtime_value + 1)])) {
-        var marker = 3;
-      } else if (true) {
-        var marker = 4;
-      } else {
-        var marker = 5;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInElseIfChain) {
-    auto* src = R"(
-const f = 1.0;
-
-const arr = array<f32, 2u>(f, f);
-
-fn F() {
-  let runtime_value = 0;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else if (f == arr[runtime_value]) {
-    var marker = 2;
-  } else if (f == arr[runtime_value + 1]) {
-    var marker = 3;
-  } else if (true) {
-    var marker = 4;
-  } else {
-    var marker = 5;
-  }
-}
-)";
-
-    auto* expect = R"(
-const f = 1.0;
-
-const arr = array<f32, 2u>(f, f);
-
-fn F() {
-  let runtime_value = 0;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else {
-    let tint_symbol : array<f32, 2u> = arr;
-    if ((f == tint_symbol[runtime_value])) {
-      var marker = 2;
-    } else {
-      let tint_symbol_1 : array<f32, 2u> = arr;
-      if ((f == tint_symbol_1[(runtime_value + 1)])) {
-        var marker = 3;
-      } else if (true) {
-        var marker = 4;
-      } else {
-        var marker = 5;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInArrayArrayConstIndex) {
-    auto* src = R"(
-fn f() {
-  var i = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0))[0][1];
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, ArrayInArrayArrayRuntimeIndex) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 1;
-  var i = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0))[runtime_value][runtime_value + 1];
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 1;
-  let tint_symbol : array<array<f32, 2u>, 2u> = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0));
-  var i = tint_symbol[runtime_value][(runtime_value + 1)];
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInArrayArrayConstIndex) {
-    auto* src = R"(
-fn f() {
-  const arr_0 = array<f32, 2u>(1.0, 2.0);
-  const arr_1 = array<f32, 2u>(3.0, 4.0);
-  const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
-  var i = arr_2[0][1];
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, LocalConstArrayInArrayArrayRuntimeIndex) {
-    auto* src = R"(
-fn f() {
-  let runtime_value = 1;
-  const arr_0 = array<f32, 2u>(1.0, 2.0);
-  const arr_1 = array<f32, 2u>(3.0, 4.0);
-  const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
-  var i = arr_2[runtime_value][runtime_value + 1];
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let runtime_value = 1;
-  const arr_0 = array<f32, 2u>(1.0, 2.0);
-  const arr_1 = array<f32, 2u>(3.0, 4.0);
-  const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
-  let tint_symbol : array<array<f32, 2u>, 2u> = arr_2;
-  var i = tint_symbol[runtime_value][(runtime_value + 1)];
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInArrayArray) {
-    auto* src = R"(
-const arr_0 = array<f32, 2u>(1.0, 2.0);
-
-const arr_1 = array<f32, 2u>(3.0, 4.0);
-
-const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
-
-fn f() {
-  let runtime_value = 1;
-  var i = arr_2[runtime_value][runtime_value + 1];
-}
-)";
-
-    auto* expect = R"(
-const arr_0 = array<f32, 2u>(1.0, 2.0);
-
-const arr_1 = array<f32, 2u>(3.0, 4.0);
-
-const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
-
-fn f() {
-  let runtime_value = 1;
-  let tint_symbol : array<array<f32, 2u>, 2u> = arr_2;
-  var i = tint_symbol[runtime_value][(runtime_value + 1)];
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, StructNested) {
-    auto* src = R"(
-struct S1 {
-  a : i32,
-};
-
-struct S2 {
-  a : i32,
-  b : S1,
-  c : i32,
-};
-
-struct S3 {
-  a : S2,
-};
-
-fn get_a(s : S3) -> S2 {
-  return s.a;
-}
-
-fn f() {
-  var x = get_a(S3(S2(1, S1(2), 3))).b.a;
-}
-)";
-
-    auto* expect = R"(
-struct S1 {
-  a : i32,
-}
-
-struct S2 {
-  a : i32,
-  b : S1,
-  c : i32,
-}
-
-struct S3 {
-  a : S2,
-}
-
-fn get_a(s : S3) -> S2 {
-  return s.a;
-}
-
-fn f() {
-  let tint_symbol : S3 = S3(S2(1, S1(2), 3));
-  var x = get_a(tint_symbol).b.a;
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, Mixed) {
-    auto* src = R"(
-struct S1 {
-  a : i32,
-};
-
-struct S2 {
-  a : array<S1, 3u>,
-};
-
-fn get_a(s : S2) -> array<S1, 3u> {
-  return s.a;
-}
-
-fn f() {
-  var x = get_a(S2(array<S1, 3u>(S1(1), S1(2), S1(3))))[1].a;
-}
-)";
-
-    auto* expect = R"(
-struct S1 {
-  a : i32,
-}
-
-struct S2 {
-  a : array<S1, 3u>,
-}
-
-fn get_a(s : S2) -> array<S1, 3u> {
-  return s.a;
-}
-
-fn f() {
-  let tint_symbol : S2 = S2(array<S1, 3u>(S1(1), S1(2), S1(3)));
-  var x = get_a(tint_symbol)[1].a;
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, Mixed_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var x = get_a(S2(array<S1, 3u>(S1(1), S1(2), S1(3))))[1].a;
-}
-
-fn get_a(s : S2) -> array<S1, 3u> {
-  return s.a;
-}
-
-struct S2 {
-  a : array<S1, 3u>,
-};
-
-struct S1 {
-  a : i32,
-};
-)";
-
-    auto* expect = R"(
-fn f() {
-  let tint_symbol : S2 = S2(array<S1, 3u>(S1(1), S1(2), S1(3)));
-  var x = get_a(tint_symbol)[1].a;
-}
-
-fn get_a(s : S2) -> array<S1, 3u> {
-  return s.a;
-}
-
-struct S2 {
-  a : array<S1, 3u>,
-}
-
-struct S1 {
-  a : i32,
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl) {
-    auto* src = R"(
-alias F = f32;
-
-fn f() {
-  var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-  var local_str = F(3.0);
-}
-
-const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-
-const module_str : F = F(2.0);
-)";
-
-    auto* expect = src;
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-  var local_str = F(3.0);
-}
-
-const module_str : F = F(2.0);
-
-alias F = f32;
-
-const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-)";
-
-    auto* expect = src;
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, ForLoopShadowing) {
-    auto* src = R"(
-fn X() {
-  var i = 10;
-  for(var f = 0; f < 10; f = f + array<i32, 1u>(i)[0]) {
-      var i = 20;
-  }
-}
-
-fn Y() {
-  var i = 10;
-  for(var f = 0; f < array<i32, 1u>(i)[0]; f = f + 1) {
-      var i = 20;
-  }
-}
-
-fn Z() {
-  var i = 10;
-  for(var f = array<i32, 1u>(i)[0]; f < 10; f = f + 1) {
-      var i = 20;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn X() {
-  var i = 10;
-  {
-    var f = 0;
-    loop {
-      if (!((f < 10))) {
-        break;
-      }
-      {
-        var i = 20;
-      }
-
-      continuing {
-        let tint_symbol : array<i32, 1u> = array<i32, 1u>(i);
-        f = (f + tint_symbol[0]);
-      }
-    }
-  }
-}
-
-fn Y() {
-  var i = 10;
-  {
-    var f = 0;
-    loop {
-      let tint_symbol_1 : array<i32, 1u> = array<i32, 1u>(i);
-      if (!((f < tint_symbol_1[0]))) {
-        break;
-      }
-      {
-        var i = 20;
-      }
-
-      continuing {
-        f = (f + 1);
-      }
-    }
-  }
-}
-
-fn Z() {
-  var i = 10;
-  {
-    let tint_symbol_2 : array<i32, 1u> = array<i32, 1u>(i);
-    var f = tint_symbol_2[0];
-    loop {
-      if (!((f < 10))) {
-        break;
-      }
-      {
-        var i = 20;
-      }
-
-      continuing {
-        f = (f + 1);
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, AssignAbstractArray) {
-    // Test that hoisting an array with abstract type still materializes to the correct type.
-    auto* src = R"(
-fn f() {
-  var arr : array<f32, 4>;
-  arr = array(1, 2, 3, 4);
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var arr : array<f32, 4>;
-  let tint_symbol : array<f32, 4u> = array(1, 2, 3, 4);
-  arr = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteInitializersToLet>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToLetTest, AssignAbstractArray_ToPhony) {
-    // Test that we do not try to hoist an abstract array expression that is the RHS of a phony
-    // assignment, as its type will not be materialized.
-    auto* src = R"(
-fn f() {
-  _ = array(1, 2, 3, 4);
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-TEST_F(PromoteInitializersToLetTest, Bug2241) {
-    auto* src = R"(
-fn f () {
-  let v = false && array<i32, array(array(6))[0][0]>()[0] == 0;
-}
-)";
-    EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.cc b/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.cc
deleted file mode 100644
index 0bc61d2..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.cc
+++ /dev/null
@@ -1,671 +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/wgsl/ast/transform/promote_side_effects_to_decl.h"
-
-#include <memory>
-#include <string>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/wgsl/ast/transform/get_insertion_point.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.h"
-#include "src/tint/lang/wgsl/ast/transform/manager.h"
-#include "src/tint/lang/wgsl/ast/traverse_expressions.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/block_statement.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/for_loop_statement.h"
-#include "src/tint/lang/wgsl/sem/if_statement.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/lang/wgsl/sem/while_statement.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::PromoteSideEffectsToDecl);
-
-namespace tint::ast::transform {
-namespace {
-
-// Base state class for common members
-class StateBase {
-  protected:
-    program::CloneContext& ctx;
-    ast::Builder& b;
-    const sem::Info& sem;
-
-    explicit StateBase(program::CloneContext& ctx_in)
-        : ctx(ctx_in), b(*ctx_in.dst), sem(ctx_in.src->Sem()) {}
-};
-
-// This first transform converts side-effecting for-loops to loops and else-ifs
-// to else {if}s so that the next transform, DecomposeSideEffects, can insert
-// hoisted expressions above their current location.
-struct SimplifySideEffectStatements : Castable<PromoteSideEffectsToDecl, Transform> {
-    ApplyResult Apply(const Program& src, const DataMap& inputs, DataMap& outputs) const override;
-};
-
-Transform::ApplyResult SimplifySideEffectStatements::Apply(const Program& src,
-                                                           const DataMap&,
-                                                           DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    bool made_changes = false;
-
-    HoistToDeclBefore hoist_to_decl_before(ctx);
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
-        if (auto* sem_expr = src.Sem().GetVal(node)) {
-            if (!sem_expr->HasSideEffects()) {
-                continue;
-            }
-
-            hoist_to_decl_before.Prepare(sem_expr);
-            made_changes = true;
-        }
-    }
-
-    if (!made_changes) {
-        return SkipTransform;
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-// Decomposes side-effecting expressions to ensure order of evaluation. This
-// handles both breaking down logical binary expressions for short-circuit
-// evaluation, as well as hoisting expressions to ensure order of evaluation.
-struct DecomposeSideEffects : Castable<PromoteSideEffectsToDecl, Transform> {
-    class CollectHoistsState;
-    class DecomposeState;
-    ApplyResult Apply(const Program& src, const DataMap& inputs, DataMap& outputs) const override;
-};
-
-// CollectHoistsState traverses the AST top-down, identifying which expressions
-// need to be hoisted to ensure order of evaluation, both those that give
-// side-effects, as well as those that receive, and returns a set of these
-// expressions.
-using ToHoistSet = std::unordered_set<const Expression*>;
-class DecomposeSideEffects::CollectHoistsState : public StateBase {
-    // Expressions to hoist because they either cause or receive side-effects.
-    ToHoistSet to_hoist;
-
-    // Used to mark expressions as not or no longer having side-effects.
-    std::unordered_set<const Expression*> no_side_effects;
-
-    // Returns true if `expr` has side-effects. Unlike invoking
-    // sem::ValueExpression::HasSideEffects(), this function takes into account whether
-    // `expr` has been hoisted, returning false in that case. Furthermore, it
-    // returns the correct result on parent expression nodes by traversing the
-    // expression tree, memoizing the results to ensure O(1) amortized lookup.
-    bool HasSideEffects(const Expression* expr) {
-        if (no_side_effects.count(expr)) {
-            return false;
-        }
-
-        return Switch(
-            expr,  //
-            [&](const CallExpression* e) -> bool { return sem.Get(e)->HasSideEffects(); },
-            [&](const BinaryExpression* e) {
-                if (HasSideEffects(e->lhs) || HasSideEffects(e->rhs)) {
-                    return true;
-                }
-                no_side_effects.insert(e);
-                return false;
-            },
-            [&](const IndexAccessorExpression* e) {
-                if (HasSideEffects(e->object) || HasSideEffects(e->index)) {
-                    return true;
-                }
-                no_side_effects.insert(e);
-                return false;
-            },
-            [&](const MemberAccessorExpression* e) {
-                if (HasSideEffects(e->object)) {
-                    return true;
-                }
-                no_side_effects.insert(e);
-                return false;
-            },
-            [&](const UnaryOpExpression* e) {
-                if (HasSideEffects(e->expr)) {
-                    return true;
-                }
-                no_side_effects.insert(e);
-                return false;
-            },
-            [&](const IdentifierExpression* e) {
-                no_side_effects.insert(e);
-                return false;
-            },
-            [&](const LiteralExpression* e) {
-                no_side_effects.insert(e);
-                return false;
-            },
-            [&](const PhonyExpression* e) {
-                no_side_effects.insert(e);
-                return false;
-            },  //
-            TINT_ICE_ON_NO_MATCH);
-    }
-
-    // Adds `e` to `to_hoist` for hoisting to a let later on.
-    void Hoist(const Expression* e) {
-        no_side_effects.insert(e);
-        to_hoist.emplace(e);
-    }
-
-    // Hoists any expressions in `maybe_hoist` and clears it
-    template <size_t N>
-    void Flush(tint::Vector<const Expression*, N>& maybe_hoist) {
-        for (auto* m : maybe_hoist) {
-            Hoist(m);
-        }
-        maybe_hoist.Clear();
-    }
-
-    // Recursive function that processes expressions for side-effects. It
-    // traverses the expression tree child before parent, left-to-right. Each call
-    // returns whether the input expression should maybe be hoisted, allowing the
-    // parent node to decide whether to hoist or not. Generally:
-    // * When 'true' is returned, the expression is added to the maybe_hoist list.
-    // * When a side-effecting expression is met, we flush the expressions in the
-    // maybe_hoist list, as they are potentially receivers of the side-effects.
-    // * For index and member accessor expressions, special care is taken to not
-    // over-hoist the lhs expressions, as these may be be chained to refer to a
-    // single memory location.
-    template <size_t N>
-    bool ProcessExpression(const Expression* expr,
-                           tint::Vector<const Expression*, N>& maybe_hoist) {
-        auto process = [&](const Expression* e) -> bool {
-            return ProcessExpression(e, maybe_hoist);
-        };
-
-        auto default_process = [&](const Expression* e) {
-            auto maybe = process(e);
-            if (maybe) {
-                maybe_hoist.Push(e);
-            }
-            if (HasSideEffects(e)) {
-                Flush(maybe_hoist);
-            }
-            return false;
-        };
-
-        auto binary_process = [&](const Expression* lhs, const Expression* rhs) {
-            // If neither side causes side-effects, but at least one receives them,
-            // let parent node hoist. This avoids over-hoisting side-effect receivers
-            // of compound binary expressions (e.g. for "((a && b) && c) && f()", we
-            // don't want to hoist each of "a", "b", and "c" separately, but want to
-            // hoist "((a && b) && c)".
-            if (!HasSideEffects(lhs) && !HasSideEffects(rhs)) {
-                auto lhs_maybe = process(lhs);
-                auto rhs_maybe = process(rhs);
-                if (lhs_maybe || rhs_maybe) {
-                    return true;
-                }
-                return false;
-            }
-
-            default_process(lhs);
-            default_process(rhs);
-            return false;
-        };
-
-        auto accessor_process = [&](const Expression* lhs, const Expression* rhs = nullptr) {
-            auto maybe = process(lhs);
-            // If lhs is a variable, let parent node hoist otherwise flush it right
-            // away. This is to avoid over-hoisting the lhs of accessor chains (e.g.
-            // for "v[a][b][c] + g()" we want to hoist all of "v[a][b][c]", not "t1 =
-            // v[a]", then "t2 = t1[b]" then "t3 = t2[c]").
-            if (maybe && HasSideEffects(lhs)) {
-                maybe_hoist.Push(lhs);
-                Flush(maybe_hoist);
-                maybe = false;
-            }
-            if (rhs) {
-                default_process(rhs);
-            }
-            return maybe;
-        };
-
-        return Switch(
-            expr,
-            [&](const CallExpression* e) -> bool {
-                // We eagerly flush any variables in maybe_hoist for the current
-                // call expression. Then we scope maybe_hoist to the processing of
-                // the call args. This ensures that given: g(c, a(0), d) we hoist
-                // 'c' because of 'a(0)', but not 'd' because there's no need, since
-                // the call to g() will be hoisted if necessary.
-                if (HasSideEffects(e)) {
-                    Flush(maybe_hoist);
-                }
-
-                TINT_SCOPED_ASSIGNMENT(maybe_hoist, {});
-                for (auto* a : e->args) {
-                    default_process(a);
-                }
-
-                // Always hoist this call, even if it has no side-effects to ensure
-                // left-to-right order of evaluation.
-                // E.g. for "no_side_effects() + side_effects()", we want to hoist
-                // no_side_effects() first.
-                return true;
-            },
-            [&](const IdentifierExpression* e) {
-                if (auto* sem_e = sem.GetVal(e)) {
-                    if (auto* var_user = sem_e->UnwrapLoad()->As<sem::VariableUser>()) {
-                        // Don't hoist constants.
-                        if (var_user->ConstantValue()) {
-                            return false;
-                        }
-                        // Don't hoist read-only variables as they cannot receive side-effects.
-                        if (var_user->Variable()->Access() == core::Access::kRead) {
-                            return false;
-                        }
-                        // Don't hoist textures / samplers as they can't be placed into a let, nor
-                        // can they have side effects.
-                        if (var_user->Variable()->Type()->IsHandle()) {
-                            return false;
-                        }
-                        return true;
-                    }
-                }
-                return false;
-            },
-            [&](const BinaryExpression* e) {
-                if (e->IsLogical() && HasSideEffects(e)) {
-                    // Don't hoist children of logical binary expressions with
-                    // side-effects. These will be handled by DecomposeState.
-                    process(e->lhs);
-                    process(e->rhs);
-                    return false;
-                }
-                return binary_process(e->lhs, e->rhs);
-            },
-            [&](const UnaryOpExpression* e) {  //
-                auto r = process(e->expr);
-                // Don't hoist address-of expressions.
-                // E.g. for "g(&b, a(0))", we hoist "a(0)" only.
-                if (e->op == core::UnaryOp::kAddressOf) {
-                    return false;
-                }
-                return r;
-            },
-            [&](const IndexAccessorExpression* e) { return accessor_process(e->object, e->index); },
-            [&](const MemberAccessorExpression* e) { return accessor_process(e->object); },
-            [&](const LiteralExpression*) {
-                // Leaf
-                return false;
-            },
-            [&](const PhonyExpression*) {
-                // Leaf
-                return false;
-            },  //
-            TINT_ICE_ON_NO_MATCH);
-    }
-
-    // Starts the recursive processing of a statement's expression(s) to hoist side-effects to lets.
-    void ProcessExpression(const Expression* expr) {
-        if (!expr) {
-            return;
-        }
-
-        tint::Vector<const Expression*, 8> maybe_hoist;
-        ProcessExpression(expr, maybe_hoist);
-    }
-
-  public:
-    explicit CollectHoistsState(program::CloneContext& ctx_in) : StateBase(ctx_in) {}
-
-    ToHoistSet Run() {
-        // Traverse all statements, recursively processing their expression tree(s)
-        // to hoist side-effects to lets.
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
-            auto* stmt = node->As<Statement>();
-            if (!stmt) {
-                continue;
-            }
-
-            Switch(
-                stmt,  //
-                [&](const AssignmentStatement* s) {
-                    tint::Vector<const Expression*, 8> maybe_hoist;
-                    ProcessExpression(s->lhs, maybe_hoist);
-                    ProcessExpression(s->rhs, maybe_hoist);
-                },
-                [&](const CallStatement* s) {  //
-                    ProcessExpression(s->expr);
-                },
-                [&](const ForLoopStatement* s) { ProcessExpression(s->condition); },
-                [&](const WhileStatement* s) { ProcessExpression(s->condition); },
-                [&](const IfStatement* s) {  //
-                    ProcessExpression(s->condition);
-                },
-                [&](const ReturnStatement* s) {  //
-                    ProcessExpression(s->value);
-                },
-                [&](const SwitchStatement* s) { ProcessExpression(s->condition); },
-                [&](const VariableDeclStatement* s) {
-                    ProcessExpression(s->variable->initializer);
-                });
-        }
-
-        return std::move(to_hoist);
-    }
-};
-
-// DecomposeState performs the actual transforming of the AST to ensure order of
-// evaluation, using the set of expressions to hoist collected by
-// CollectHoistsState.
-class DecomposeSideEffects::DecomposeState : public StateBase {
-    ToHoistSet to_hoist;
-
-    // Returns true if `binary_expr` should be decomposed for short-circuit eval.
-    bool IsLogicalWithSideEffects(const BinaryExpression* binary_expr) {
-        return binary_expr->IsLogical() && (sem.GetVal(binary_expr->lhs)->HasSideEffects() ||
-                                            sem.GetVal(binary_expr->rhs)->HasSideEffects());
-    }
-
-    // Recursive function used to decompose an expression for short-circuit eval.
-    template <size_t N>
-    const Expression* Decompose(const Expression* expr,
-                                tint::Vector<const Statement*, N>* curr_stmts) {
-        // Helper to avoid passing in same args.
-        auto decompose = [&](auto& e) { return Decompose(e, curr_stmts); };
-
-        // Clones `expr`, possibly hoisting it to a let.
-        auto clone_maybe_hoisted = [&](const Expression* e) -> const Expression* {
-            if (to_hoist.count(e)) {
-                auto name = b.Symbols().New();
-                auto* ty = sem.GetVal(e)->Type();
-                auto* v = b.Let(name, Transform::CreateASTTypeFor(ctx, ty), ctx.Clone(e));
-                auto* decl = b.Decl(v);
-                curr_stmts->Push(decl);
-                return b.Expr(name);
-            }
-            return ctx.Clone(e);
-        };
-
-        return Switch(
-            expr,
-            [&](const BinaryExpression* bin_expr) -> const Expression* {
-                if (!IsLogicalWithSideEffects(bin_expr)) {
-                    // No short-circuit, emit usual binary expr
-                    ctx.Replace(bin_expr->lhs, decompose(bin_expr->lhs));
-                    ctx.Replace(bin_expr->rhs, decompose(bin_expr->rhs));
-                    return clone_maybe_hoisted(bin_expr);
-                }
-
-                // Decompose into ifs to implement short-circuiting
-                // For example, 'let r = a && b' becomes:
-                //
-                // var temp = a;
-                // if (temp) {
-                //   temp = b;
-                // }
-                // let r = temp;
-                //
-                // and similarly, 'let r = a || b' becomes:
-                //
-                // var temp = a;
-                // if (!temp) {
-                //     temp = b;
-                // }
-                // let r = temp;
-                //
-                // Further, compound logical binary expressions are also handled
-                // recursively, for example, 'let r = (a && (b && c))' becomes:
-                //
-                // var temp = a;
-                // if (temp) {
-                //     var temp2 = b;
-                //     if (temp2) {
-                //         temp2 = c;
-                //     }
-                //     temp = temp2;
-                // }
-                // let r = temp;
-
-                auto name = b.Sym();
-                curr_stmts->Push(b.Decl(b.Var(name, decompose(bin_expr->lhs))));
-
-                const Expression* if_cond = nullptr;
-                if (bin_expr->IsLogicalOr()) {
-                    if_cond = b.Not(name);
-                } else {
-                    if_cond = b.Expr(name);
-                }
-
-                const BlockStatement* if_body = nullptr;
-                {
-                    tint::Vector<const Statement*, N> stmts;
-                    TINT_SCOPED_ASSIGNMENT(curr_stmts, &stmts);
-                    auto* new_rhs = decompose(bin_expr->rhs);
-                    curr_stmts->Push(b.Assign(name, new_rhs));
-                    if_body = b.Block(std::move(*curr_stmts));
-                }
-
-                curr_stmts->Push(b.If(if_cond, if_body));
-
-                return b.Expr(name);
-            },
-            [&](const IndexAccessorExpression* idx) {
-                ctx.Replace(idx->object, decompose(idx->object));
-                ctx.Replace(idx->index, decompose(idx->index));
-                return clone_maybe_hoisted(idx);
-            },
-            [&](const CallExpression* call) {
-                for (auto* a : call->args) {
-                    ctx.Replace(a, decompose(a));
-                }
-                return clone_maybe_hoisted(call);
-            },
-            [&](const MemberAccessorExpression* member) {
-                ctx.Replace(member->object, decompose(member->object));
-                return clone_maybe_hoisted(member);
-            },
-            [&](const UnaryOpExpression* unary) {
-                ctx.Replace(unary->expr, decompose(unary->expr));
-                return clone_maybe_hoisted(unary);
-            },
-            [&](const LiteralExpression* lit) {
-                return clone_maybe_hoisted(lit);  // Leaf expression, just clone as is
-            },
-            [&](const IdentifierExpression* id) {
-                return clone_maybe_hoisted(id);  // Leaf expression, just clone as is
-            },
-            [&](const PhonyExpression* phony) {
-                return clone_maybe_hoisted(phony);  // Leaf expression, just clone as is
-            },                                      //
-            TINT_ICE_ON_NO_MATCH);
-    }
-
-    // Inserts statements in `stmts` before `stmt`
-    template <size_t N>
-    void InsertBefore(tint::Vector<const Statement*, N>& stmts, const Statement* stmt) {
-        if (!stmts.IsEmpty()) {
-            auto ip = utils::GetInsertionPoint(ctx, stmt);
-            for (auto* s : stmts) {
-                ctx.InsertBefore(ip.first->Declaration()->statements, ip.second, s);
-            }
-        }
-    }
-
-    // Decomposes expressions of `stmt`, returning a replacement statement or
-    // nullptr if not replacing it.
-    const Statement* DecomposeStatement(const Statement* stmt) {
-        return Switch(
-            stmt,
-            [&](const AssignmentStatement* s) -> const Statement* {
-                if (!sem.GetVal(s->lhs)->HasSideEffects() &&
-                    !sem.GetVal(s->rhs)->HasSideEffects()) {
-                    return nullptr;
-                }
-                // lhs before rhs
-                tint::Vector<const Statement*, 8> stmts;
-                ctx.Replace(s->lhs, Decompose(s->lhs, &stmts));
-                ctx.Replace(s->rhs, Decompose(s->rhs, &stmts));
-                InsertBefore(stmts, s);
-                return ctx.CloneWithoutTransform(s);
-            },
-            [&](const CallStatement* s) -> const Statement* {
-                if (!sem.Get(s->expr)->HasSideEffects()) {
-                    return nullptr;
-                }
-                tint::Vector<const Statement*, 8> stmts;
-                ctx.Replace(s->expr, Decompose(s->expr, &stmts));
-                InsertBefore(stmts, s);
-                return ctx.CloneWithoutTransform(s);
-            },
-            [&](const ForLoopStatement* s) -> const Statement* {
-                if (!s->condition || !sem.GetVal(s->condition)->HasSideEffects()) {
-                    return nullptr;
-                }
-                tint::Vector<const Statement*, 8> stmts;
-                ctx.Replace(s->condition, Decompose(s->condition, &stmts));
-                InsertBefore(stmts, s);
-                return ctx.CloneWithoutTransform(s);
-            },
-            [&](const WhileStatement* s) -> const Statement* {
-                if (!sem.GetVal(s->condition)->HasSideEffects()) {
-                    return nullptr;
-                }
-                tint::Vector<const Statement*, 8> stmts;
-                ctx.Replace(s->condition, Decompose(s->condition, &stmts));
-                InsertBefore(stmts, s);
-                return ctx.CloneWithoutTransform(s);
-            },
-            [&](const IfStatement* s) -> const Statement* {
-                if (!sem.GetVal(s->condition)->HasSideEffects()) {
-                    return nullptr;
-                }
-                tint::Vector<const Statement*, 8> stmts;
-                ctx.Replace(s->condition, Decompose(s->condition, &stmts));
-                InsertBefore(stmts, s);
-                return ctx.CloneWithoutTransform(s);
-            },
-            [&](const ReturnStatement* s) -> const Statement* {
-                if (!s->value || !sem.GetVal(s->value)->HasSideEffects()) {
-                    return nullptr;
-                }
-                tint::Vector<const Statement*, 8> stmts;
-                ctx.Replace(s->value, Decompose(s->value, &stmts));
-                InsertBefore(stmts, s);
-                return ctx.CloneWithoutTransform(s);
-            },
-            [&](const SwitchStatement* s) -> const Statement* {
-                if (!sem.Get(s->condition)) {
-                    return nullptr;
-                }
-                tint::Vector<const Statement*, 8> stmts;
-                ctx.Replace(s->condition, Decompose(s->condition, &stmts));
-                InsertBefore(stmts, s);
-                return ctx.CloneWithoutTransform(s);
-            },
-            [&](const VariableDeclStatement* s) -> const Statement* {
-                auto* var = s->variable;
-                if (!var->initializer || !sem.GetVal(var->initializer)->HasSideEffects()) {
-                    return nullptr;
-                }
-                tint::Vector<const Statement*, 8> stmts;
-                ctx.Replace(var->initializer, Decompose(var->initializer, &stmts));
-                InsertBefore(stmts, s);
-                return b.Decl(ctx.CloneWithoutTransform(var));
-            },
-            [](Default) -> const Statement* {
-                // Other statement types don't have expressions
-                return nullptr;
-            });
-    }
-
-  public:
-    explicit DecomposeState(program::CloneContext& ctx_in, ToHoistSet to_hoist_in)
-        : StateBase(ctx_in), to_hoist(std::move(to_hoist_in)) {}
-
-    void Run() {
-        // We replace all BlockStatements as this allows us to iterate over the
-        // block statements and ctx.InsertBefore hoisted declarations on them.
-        ctx.ReplaceAll([&](const BlockStatement* block) -> const Statement* {
-            for (auto* stmt : block->statements) {
-                if (auto* new_stmt = DecomposeStatement(stmt)) {
-                    ctx.Replace(stmt, new_stmt);
-                }
-
-                // Handle for loops, as they are the only other AST node that
-                // contains statements outside of BlockStatements.
-                if (auto* fl = stmt->As<ForLoopStatement>()) {
-                    if (auto* new_stmt = DecomposeStatement(fl->initializer)) {
-                        ctx.Replace(fl->initializer, new_stmt);
-                    }
-                    if (auto* new_stmt = DecomposeStatement(fl->continuing)) {
-                        ctx.Replace(fl->continuing, new_stmt);
-                    }
-                }
-            }
-            return nullptr;
-        });
-    }
-};
-
-Transform::ApplyResult DecomposeSideEffects::Apply(const Program& src,
-                                                   const DataMap&,
-                                                   DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    // First collect side-effecting expressions to hoist
-    CollectHoistsState collect_hoists_state{ctx};
-    auto to_hoist = collect_hoists_state.Run();
-
-    // Now decompose these expressions
-    DecomposeState decompose_state{ctx, std::move(to_hoist)};
-    decompose_state.Run();
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace
-
-PromoteSideEffectsToDecl::PromoteSideEffectsToDecl() = default;
-PromoteSideEffectsToDecl::~PromoteSideEffectsToDecl() = default;
-
-Transform::ApplyResult PromoteSideEffectsToDecl::Apply(const Program& src,
-                                                       const DataMap& inputs,
-                                                       DataMap& outputs) const {
-    Manager manager;
-    manager.Add<SimplifySideEffectStatements>();
-    manager.Add<DecomposeSideEffects>();
-    return manager.Run(src, inputs, outputs);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h b/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h
deleted file mode 100644
index c488745..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h
+++ /dev/null
@@ -1,57 +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_WGSL_AST_TRANSFORM_PROMOTE_SIDE_EFFECTS_TO_DECL_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_SIDE_EFFECTS_TO_DECL_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// A transform that hoists expressions with side-effects to variable
-/// declarations before the statement of usage with the goal of ensuring
-/// left-to-right order of evaluation, while respecting short-circuit
-/// evaluation.
-/// As `@diagnostic` statements can be decomposed, this transform requires that the
-/// DisableUniformityAnalysis transform is run first.
-class PromoteSideEffectsToDecl final : public Castable<PromoteSideEffectsToDecl, Transform> {
-  public:
-    /// Constructor
-    PromoteSideEffectsToDecl();
-
-    /// Destructor
-    ~PromoteSideEffectsToDecl() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_PROMOTE_SIDE_EFFECTS_TO_DECL_H_
diff --git a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc b/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc
deleted file mode 100644
index e89dad0..0000000
--- a/src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc
+++ /dev/null
@@ -1,4264 +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/wgsl/ast/transform/promote_side_effects_to_decl.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using PromoteSideEffectsToDeclTest = TransformTest;
-
-TEST_F(PromoteSideEffectsToDeclTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = "";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Unary_Arith_SE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  let r = -(a(0));
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_BothSE) {
-    auto* src = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn b() -> i32 {
-  return 1;
-}
-
-fn f() {
-  let r = a() + b();
-}
-)";
-
-    auto* expect = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn b() -> i32 {
-  return 1;
-}
-
-fn f() {
-  let tint_symbol : i32 = a();
-  let tint_symbol_1 : i32 = b();
-  let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_LeftSE) {
-    auto* src = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let r = a() + b;
-}
-)";
-
-    auto* expect = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = a();
-  let r = (tint_symbol + b);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_RightSE) {
-    auto* src = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let r = b + a();
-}
-)";
-
-    auto* expect = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = b;
-  let tint_symbol_1 : i32 = a();
-  let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_LeftmostSE) {
-    auto* src = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  var c = 1;
-  var d = 1;
-  let r = a() + b + c + d;
-}
-)";
-
-    auto* expect = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  var c = 1;
-  var d = 1;
-  let tint_symbol : i32 = a();
-  let r = (((tint_symbol + b) + c) + d);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_RightmostSE) {
-    auto* src = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  var c = 1;
-  var d = 1;
-  let r = b + c + d + a();
-}
-)";
-
-    auto* expect = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  var c = 1;
-  var d = 1;
-  let tint_symbol : i32 = ((b + c) + d);
-  let tint_symbol_1 : i32 = a();
-  let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_MiddleSE) {
-    auto* src = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  var c = 1;
-  var d = 1;
-  var e = 1;
-  let r = b + c + a() + d + e;
-}
-)";
-
-    auto* expect = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  var c = 1;
-  var d = 1;
-  var e = 1;
-  let tint_symbol : i32 = (b + c);
-  let tint_symbol_1 : i32 = a();
-  let r = (((tint_symbol + tint_symbol_1) + d) + e);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_ThreeSE) {
-    auto* src = R"(
-fn a(v : i32) -> i32 {
-  return v;
-}
-
-fn f() {
-  let r = a(0) + a(1) + a(2);
-}
-)";
-
-    auto* expect = R"(
-fn a(v : i32) -> i32 {
-  return v;
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  let tint_symbol_2 : i32 = a(2);
-  let r = ((tint_symbol + tint_symbol_1) + tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_NoRecvSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  let r = ((((1 + a(0)) - 2) + 3) - 4);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  let r = ((((1 + tint_symbol) - 2) + 3) - 4);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_RecvSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let r = a(0) + 1 + b + a(1) + 2;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = b;
-  let tint_symbol_2 : i32 = a(1);
-  let r = ((((tint_symbol + 1) + tint_symbol_1) + tint_symbol_2) + 2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_ConstAndSEAndVar) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn main() {
-  var b = 1;
-  var c = 1;
-  let r = 1 + a(0) + b;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn main() {
-  var b = 1;
-  var c = 1;
-  let tint_symbol : i32 = a(0);
-  let r = ((1 + tint_symbol) + b);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_VarAndSEAndConst) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn main() {
-  var b = 1;
-  let r = b + a(0) + 1;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn main() {
-  var b = 1;
-  let tint_symbol : i32 = b;
-  let tint_symbol_1 : i32 = a(0);
-  let r = ((tint_symbol + tint_symbol_1) + 1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Constants_SEAndVarAndConstAndVar) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn main() {
-  var b = 1;
-  var c = 1;
-  let r = a(0) + b + 1 + c;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn main() {
-  var b = 1;
-  var c = 1;
-  let tint_symbol : i32 = a(0);
-  let r = (((tint_symbol + b) + 1) + c);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Builtins_WithSE) {
-    auto* src = R"(
-struct SB {
-  a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn f() {
-  var b = 0;
-  let r = atomicAdd(&sb.a, 123) + b;
-}
-)";
-
-    auto* expect = R"(
-struct SB {
-  a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn f() {
-  var b = 0;
-  let tint_symbol : i32 = atomicAdd(&(sb.a), 123);
-  let r = (tint_symbol + b);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Builtins_NoSEAndVar) {
-    auto* src = R"(
-struct SB {
-  a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn f() {
-  var b = 0;
-  let r = (atomicLoad(&(sb.a)) + b);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Builtins_NoSEAndSE) {
-    auto* src = R"(
-struct SB {
-  a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 0;
-  let r = (atomicLoad(&(sb.a)) + a(0));
-}
-)";
-
-    auto* expect = R"(
-struct SB {
-  a : atomic<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 0;
-  let tint_symbol : i32 = atomicLoad(&(sb.a));
-  let tint_symbol_1 : i32 = a(0);
-  let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_Vector_RightSE) {
-    auto* src = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b : vec3<i32>;
-  var c : i32;
-  let r = b[c] + a();
-}
-)";
-
-    auto* expect = R"(
-fn a() -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b : vec3<i32>;
-  var c : i32;
-  let tint_symbol : i32 = c;
-  let tint_symbol_1 : i32 = b[tint_symbol];
-  let tint_symbol_2 : i32 = a();
-  let r = (tint_symbol_1 + tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InCall) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn g(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let r = g(a(0)) - g(b + a(1));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn g(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = g(tint_symbol);
-  let tint_symbol_2 : i32 = b;
-  let tint_symbol_3 : i32 = a(1);
-  let tint_symbol_4 : i32 = g((tint_symbol_2 + tint_symbol_3));
-  let r = (tint_symbol_1 - tint_symbol_4);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InTypeInit) {
-    auto* src = R"(
-
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let r = i32(a(0)) + i32(a(1) + b) - i32(a(2) - a(3));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = i32(tint_symbol);
-  let tint_symbol_2 : i32 = a(1);
-  let tint_symbol_3 : i32 = i32((tint_symbol_2 + b));
-  let tint_symbol_4 : i32 = a(2);
-  let tint_symbol_5 : i32 = a(3);
-  let tint_symbol_6 : i32 = i32((tint_symbol_4 - tint_symbol_5));
-  let r = ((tint_symbol_1 + tint_symbol_3) - tint_symbol_6);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InTypeConversion) {
-    auto* src = R"(
-
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1u;
-  let r = u32(a(0)) + u32(a(1)) - b;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1u;
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : u32 = u32(tint_symbol);
-  let tint_symbol_2 : i32 = a(1);
-  let tint_symbol_3 : u32 = u32(tint_symbol_2);
-  let r = ((tint_symbol_1 + tint_symbol_3) - b);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InIntrinsic) {
-    auto* src = R"(
-
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let r = abs(a(0)) + abs(a(1) + b) - abs(a(2) + a(3));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = abs(tint_symbol);
-  let tint_symbol_2 : i32 = a(1);
-  let tint_symbol_3 : i32 = abs((tint_symbol_2 + b));
-  let tint_symbol_4 : i32 = a(2);
-  let tint_symbol_5 : i32 = a(3);
-  let tint_symbol_6 : i32 = abs((tint_symbol_4 + tint_symbol_5));
-  let r = ((tint_symbol_1 + tint_symbol_3) - tint_symbol_6);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InMemberAccessor) {
-    auto* src = R"(
-
-struct S {
-  v : i32,
-}
-
-fn a(i : i32) -> S {
-  return S();
-}
-
-fn f() {
-  var b = 1;
-  let r = a(0).v + b + a(1).v;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  v : i32,
-}
-
-fn a(i : i32) -> S {
-  return S();
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : S = a(0);
-  let tint_symbol_1 : i32 = b;
-  let tint_symbol_2 : S = a(1);
-  let r = ((tint_symbol.v + tint_symbol_1) + tint_symbol_2.v);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InUnary) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let r = -a(0) + -(b + a(1));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = -(a(0));
-  let tint_symbol_1 : i32 = b;
-  let tint_symbol_2 : i32 = a(1);
-  let r = (tint_symbol + -((tint_symbol_1 + tint_symbol_2)));
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InBitcast) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let r = bitcast<u32>(a(0) + a(1));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  let r = bitcast<u32>((tint_symbol + tint_symbol_1));
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InForLoopInit) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  for(var r = a(0) + b; ; ) {
-    var marker = 0;
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  {
-    let tint_symbol : i32 = a(0);
-    var r = (tint_symbol + b);
-    loop {
-      {
-        var marker = 0;
-        break;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InForLoopCond) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  for(; a(0) + b > 0;) {
-    var marker = 0;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  loop {
-    let tint_symbol : i32 = a(0);
-    if (!(((tint_symbol + b) > 0))) {
-      break;
-    }
-    {
-      var marker = 0;
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InForLoopCont) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  var r = 0;
-  for(; ; r = a(0) + b) {
-    var marker = 0;
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  var r = 0;
-  loop {
-    {
-      var marker = 0;
-      break;
-    }
-
-    continuing {
-      let tint_symbol : i32 = a(0);
-      r = (tint_symbol + b);
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InForLoopInitCondCont) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  var c = 2;
-  var d = 3;
-  var r = 0;
-  for(var r = a(0) + b; a(1) + c > 0; r = a(2) + d) {
-    var marker = 0;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  var c = 2;
-  var d = 3;
-  var r = 0;
-  {
-    let tint_symbol : i32 = a(0);
-    var r = (tint_symbol + b);
-    loop {
-      let tint_symbol_1 : i32 = a(1);
-      if (!(((tint_symbol_1 + c) > 0))) {
-        break;
-      }
-      {
-        var marker = 0;
-      }
-
-      continuing {
-        let tint_symbol_2 : i32 = a(2);
-        r = (tint_symbol_2 + d);
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InWhileCond) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  while(a(0) + b > 0) {
-    var marker = 0;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  loop {
-    let tint_symbol : i32 = a(0);
-    if (!(((tint_symbol + b) > 0))) {
-      break;
-    }
-    {
-      var marker = 0;
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InElseIf) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  if (true) {
-    var marker = 0;
-  } else if (a(0) + b > 0) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  if (true) {
-    var marker = 0;
-  } else {
-    let tint_symbol : i32 = a(0);
-    if (((tint_symbol + b) > 0)) {
-      var marker = 1;
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InElseIfChain) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else if (a(0) + b > 0) {
-    var marker = 2;
-  } else if (a(1) + a(2) > 0) {
-    var marker = 3;
-  } else if (true) {
-    var marker = 4;
-  } else {
-    var marker = 5;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else {
-    let tint_symbol : i32 = a(0);
-    if (((tint_symbol + b) > 0)) {
-      var marker = 2;
-    } else {
-      let tint_symbol_1 : i32 = a(1);
-      let tint_symbol_2 : i32 = a(2);
-      if (((tint_symbol_1 + tint_symbol_2) > 0)) {
-        var marker = 3;
-      } else if (true) {
-        var marker = 4;
-      } else {
-        var marker = 5;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InReturn) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() -> i32 {
-  var b = 1;
-  return b + a(0);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() -> i32 {
-  var b = 1;
-  let tint_symbol : i32 = b;
-  let tint_symbol_1 : i32 = a(0);
-  return (tint_symbol + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Arith_InSwitch) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  switch (b + a(0)) {
-    default: {
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = b;
-  let tint_symbol_1 : i32 = a(0);
-  switch((tint_symbol + tint_symbol_1)) {
-    default: {
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_LeftSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let r = a(0) && b;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var tint_symbol = a(0);
-  if (tint_symbol) {
-    tint_symbol = b;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_RightSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let r = b && a(0);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var tint_symbol = b;
-  if (tint_symbol) {
-    tint_symbol = a(0);
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_BothSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  let r = a(0) && a(1);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var tint_symbol = a(0);
-  if (tint_symbol) {
-    tint_symbol = a(1);
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_LeftmostSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  let r = a(0) && b && c && d;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  var tint_symbol_2 = a(0);
-  if (tint_symbol_2) {
-    tint_symbol_2 = b;
-  }
-  var tint_symbol_1 = tint_symbol_2;
-  if (tint_symbol_1) {
-    tint_symbol_1 = c;
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    tint_symbol = d;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_RightmostSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  let r = b && c && d && a(0);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  var tint_symbol = ((b && c) && d);
-  if (tint_symbol) {
-    tint_symbol = a(0);
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_MiddleSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  let r = b && c && a(0) && c && d;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  var tint_symbol_2 = (b && c);
-  if (tint_symbol_2) {
-    tint_symbol_2 = a(0);
-  }
-  var tint_symbol_1 = tint_symbol_2;
-  if (tint_symbol_1) {
-    tint_symbol_1 = c;
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    tint_symbol = d;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_NoRecvSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  let r = true && a(0) && false && a(1);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var tint_symbol_2 = true;
-  if (tint_symbol_2) {
-    tint_symbol_2 = a(0);
-  }
-  var tint_symbol_1 = tint_symbol_2;
-  if (tint_symbol_1) {
-    tint_symbol_1 = false;
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    tint_symbol = a(1);
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_RecvSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let r = b && true && a(0) && false && a(1);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var tint_symbol_2 = (b && true);
-  if (tint_symbol_2) {
-    tint_symbol_2 = a(0);
-  }
-  var tint_symbol_1 = tint_symbol_2;
-  if (tint_symbol_1) {
-    tint_symbol_1 = false;
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    tint_symbol = a(1);
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_ConstAndSEAndVar) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn main() {
-  var b = true;
-  var c = true;
-  let r = true && a(0) && b;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn main() {
-  var b = true;
-  var c = true;
-  var tint_symbol_1 = true;
-  if (tint_symbol_1) {
-    tint_symbol_1 = a(0);
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    tint_symbol = b;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_VarAndSEAndConst) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn main() {
-  var b = true;
-  let r = b && a(0) && true;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn main() {
-  var b = true;
-  var tint_symbol_1 = b;
-  if (tint_symbol_1) {
-    tint_symbol_1 = a(0);
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    tint_symbol = true;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_Constants_SEAndVarAndConstAndVar) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn main() {
-  var b = true;
-  var c = true;
-  let r = a(0) && b && true && c;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn main() {
-  var b = true;
-  var c = true;
-  var tint_symbol_2 = a(0);
-  if (tint_symbol_2) {
-    tint_symbol_2 = b;
-  }
-  var tint_symbol_1 = tint_symbol_2;
-  if (tint_symbol_1) {
-    tint_symbol_1 = true;
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    tint_symbol = c;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_MixedSE) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  let r = (b && a(0)) || (c && a(1) && c && d) || a(2);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  var tint_symbol_2 = b;
-  if (tint_symbol_2) {
-    tint_symbol_2 = a(0);
-  }
-  var tint_symbol_1 = tint_symbol_2;
-  if (!(tint_symbol_1)) {
-    var tint_symbol_5 = c;
-    if (tint_symbol_5) {
-      tint_symbol_5 = a(1);
-    }
-    var tint_symbol_4 = tint_symbol_5;
-    if (tint_symbol_4) {
-      tint_symbol_4 = c;
-    }
-    var tint_symbol_3 = tint_symbol_4;
-    if (tint_symbol_3) {
-      tint_symbol_3 = d;
-    }
-    tint_symbol_1 = tint_symbol_3;
-  }
-  var tint_symbol = tint_symbol_1;
-  if (!(tint_symbol)) {
-    tint_symbol = a(2);
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_NestedAnds) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  let r = a(0) && (a(1) && (a(2) && (a(3) && (a(4) && a(5)))));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var tint_symbol = a(0);
-  if (tint_symbol) {
-    var tint_symbol_1 = a(1);
-    if (tint_symbol_1) {
-      var tint_symbol_2 = a(2);
-      if (tint_symbol_2) {
-        var tint_symbol_3 = a(3);
-        if (tint_symbol_3) {
-          var tint_symbol_4 = a(4);
-          if (tint_symbol_4) {
-            tint_symbol_4 = a(5);
-          }
-          tint_symbol_3 = tint_symbol_4;
-        }
-        tint_symbol_2 = tint_symbol_3;
-      }
-      tint_symbol_1 = tint_symbol_2;
-    }
-    tint_symbol = tint_symbol_1;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_NestedOrs) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  let r = a(0) || (a(1) || (a(2) || (a(3) || (a(4) || a(5)))));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var tint_symbol = a(0);
-  if (!(tint_symbol)) {
-    var tint_symbol_1 = a(1);
-    if (!(tint_symbol_1)) {
-      var tint_symbol_2 = a(2);
-      if (!(tint_symbol_2)) {
-        var tint_symbol_3 = a(3);
-        if (!(tint_symbol_3)) {
-          var tint_symbol_4 = a(4);
-          if (!(tint_symbol_4)) {
-            tint_symbol_4 = a(5);
-          }
-          tint_symbol_3 = tint_symbol_4;
-        }
-        tint_symbol_2 = tint_symbol_3;
-      }
-      tint_symbol_1 = tint_symbol_2;
-    }
-    tint_symbol = tint_symbol_1;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_MultipleStatements) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let r1 = b && a(0);
-  let r2 = a(1) || b;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var tint_symbol = b;
-  if (tint_symbol) {
-    tint_symbol = a(0);
-  }
-  let r1 = tint_symbol;
-  var tint_symbol_1 = a(1);
-  if (!(tint_symbol_1)) {
-    tint_symbol_1 = b;
-  }
-  let r2 = tint_symbol_1;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InCall) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn g(v : bool) -> bool {
-  return v;
-}
-
-fn f() {
-  var b = true;
-  g(b && a(1));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn g(v : bool) -> bool {
-  return v;
-}
-
-fn f() {
-  var b = true;
-  var tint_symbol = b;
-  if (tint_symbol) {
-    tint_symbol = a(1);
-  }
-  g(tint_symbol);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InTypeInit) {
-    auto* src = R"(
-
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let r = (bool(a(0)) && bool(a(1) && b)) || bool(a(2) && a(3));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let tint_symbol_2 : bool = a(0);
-  var tint_symbol_1 = bool(tint_symbol_2);
-  if (tint_symbol_1) {
-    var tint_symbol_3 = a(1);
-    if (tint_symbol_3) {
-      tint_symbol_3 = b;
-    }
-    tint_symbol_1 = bool(tint_symbol_3);
-  }
-  var tint_symbol = tint_symbol_1;
-  if (!(tint_symbol)) {
-    var tint_symbol_4 = a(2);
-    if (tint_symbol_4) {
-      tint_symbol_4 = a(3);
-    }
-    tint_symbol = bool(tint_symbol_4);
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InTypeConversion) {
-    auto* src = R"(
-
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = true;
-  let r = (bool(a(0)) && bool(a(1))) || b;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = true;
-  let tint_symbol_2 : i32 = a(0);
-  var tint_symbol_1 = bool(tint_symbol_2);
-  if (tint_symbol_1) {
-    let tint_symbol_3 : i32 = a(1);
-    tint_symbol_1 = bool(tint_symbol_3);
-  }
-  var tint_symbol = tint_symbol_1;
-  if (!(tint_symbol)) {
-    tint_symbol = b;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-// Make sure we process logical binary expressions of non-logical binary
-// expressions.
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_OfNonLogical) {
-    auto* src = R"(
-
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let r = bool(a(0) == b) && bool(a(1) == b);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol_1 : i32 = a(0);
-  var tint_symbol = bool((tint_symbol_1 == b));
-  if (tint_symbol) {
-    let tint_symbol_2 : i32 = a(1);
-    tint_symbol = bool((tint_symbol_2 == b));
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InIntrinsic) {
-    auto* src = R"(
-
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let r = (all(a(0)) && all(a(1) && b)) || all(a(2) && a(3));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let tint_symbol_2 : bool = a(0);
-  var tint_symbol_1 = all(tint_symbol_2);
-  if (tint_symbol_1) {
-    var tint_symbol_3 = a(1);
-    if (tint_symbol_3) {
-      tint_symbol_3 = b;
-    }
-    tint_symbol_1 = all(tint_symbol_3);
-  }
-  var tint_symbol = tint_symbol_1;
-  if (!(tint_symbol)) {
-    var tint_symbol_4 = a(2);
-    if (tint_symbol_4) {
-      tint_symbol_4 = a(3);
-    }
-    tint_symbol = all(tint_symbol_4);
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InMemberAccessor) {
-    auto* src = R"(
-
-struct S {
-  v : bool,
-}
-
-fn a(i : i32) -> S {
-  return S();
-}
-
-fn f() {
-  var b = true;
-  let r = a(0).v && b && a(1).v;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  v : bool,
-}
-
-fn a(i : i32) -> S {
-  return S();
-}
-
-fn f() {
-  var b = true;
-  let tint_symbol_2 : S = a(0);
-  var tint_symbol_1 = tint_symbol_2.v;
-  if (tint_symbol_1) {
-    tint_symbol_1 = b;
-  }
-  var tint_symbol = tint_symbol_1;
-  if (tint_symbol) {
-    let tint_symbol_3 : S = a(1);
-    tint_symbol = tint_symbol_3.v;
-  }
-  let r = tint_symbol;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InUnary) {
-    auto* src = R"(
-
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let r = !(b || a(1));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var tint_symbol = b;
-  if (!(tint_symbol)) {
-    tint_symbol = a(1);
-  }
-  let r = !(tint_symbol);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InBitcast) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  let r = bitcast<u32>(i32(a(0) && a(1)));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var tint_symbol = a(0);
-  if (tint_symbol) {
-    tint_symbol = a(1);
-  }
-  let tint_symbol_1 : i32 = i32(tint_symbol);
-  let r = bitcast<u32>(tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InForLoopInit) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  for(var r = a(0) && b; ; ) {
-    var marker = 0;
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  {
-    var tint_symbol = a(0);
-    if (tint_symbol) {
-      tint_symbol = b;
-    }
-    var r = tint_symbol;
-    loop {
-      {
-        var marker = 0;
-        break;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InForLoopCond) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  for(; a(0) && b;) {
-    var marker = 0;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  loop {
-    var tint_symbol = a(0);
-    if (tint_symbol) {
-      tint_symbol = b;
-    }
-    if (!(tint_symbol)) {
-      break;
-    }
-    {
-      var marker = 0;
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InForLoopCont) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var r = true;
-  for(; ; r = a(0) && b) {
-    var marker = 0;
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var r = true;
-  loop {
-    {
-      var marker = 0;
-      break;
-    }
-
-    continuing {
-      var tint_symbol = a(0);
-      if (tint_symbol) {
-        tint_symbol = b;
-      }
-      r = tint_symbol;
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InForLoopInitCondCont) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  var r = true;
-  for(var r = a(0) && b; a(1) && c; r = a(2) && d) {
-    var marker = 0;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  var c = true;
-  var d = true;
-  var r = true;
-  {
-    var tint_symbol = a(0);
-    if (tint_symbol) {
-      tint_symbol = b;
-    }
-    var r = tint_symbol;
-    loop {
-      var tint_symbol_1 = a(1);
-      if (tint_symbol_1) {
-        tint_symbol_1 = c;
-      }
-      if (!(tint_symbol_1)) {
-        break;
-      }
-      {
-        var marker = 0;
-      }
-
-      continuing {
-        var tint_symbol_2 = a(2);
-        if (tint_symbol_2) {
-          tint_symbol_2 = d;
-        }
-        r = tint_symbol_2;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InWhileCond) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  while(a(0) && b) {
-    var marker = 0;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  loop {
-    var tint_symbol = a(0);
-    if (tint_symbol) {
-      tint_symbol = b;
-    }
-    if (!(tint_symbol)) {
-      break;
-    }
-    {
-      var marker = 0;
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InElseIf) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  if (true) {
-    var marker = 0;
-  } else if (a(0) && b) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  if (true) {
-    var marker = 0;
-  } else {
-    var tint_symbol = a(0);
-    if (tint_symbol) {
-      tint_symbol = b;
-    }
-    if (tint_symbol) {
-      var marker = 1;
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Logical_InElseIfChain) {
-    auto* src = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else if (a(0) && b) {
-    var marker = 2;
-  } else if (a(1) && a(2)) {
-    var marker = 3;
-  } else if (true) {
-    var marker = 4;
-  } else {
-    var marker = 5;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> bool {
-  return true;
-}
-
-fn f() {
-  var b = true;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else {
-    var tint_symbol = a(0);
-    if (tint_symbol) {
-      tint_symbol = b;
-    }
-    if (tint_symbol) {
-      var marker = 2;
-    } else {
-      var tint_symbol_1 = a(1);
-      if (tint_symbol_1) {
-        tint_symbol_1 = a(2);
-      }
-      if (tint_symbol_1) {
-        var marker = 3;
-      } else if (true) {
-        var marker = 4;
-      } else {
-        var marker = 5;
-      }
-    }
-  }
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_NoSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  var c = 1;
-  let r = g(b, c, 3);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_OneSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let r = g(a(0), b, 3);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = a(0);
-  let r = g(tint_symbol, b, 3);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_AllSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  let r = g(a(0), a(1), a(2));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  let tint_symbol_2 : i32 = a(2);
-  let r = g(tint_symbol, tint_symbol_1, tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_MiddleNotSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
-  return 1;
-}
-
-
-fn f() {
-  var b = 1;
-  let r = g(a(0), b, a(1));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(a : i32, b : i32, c : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 1;
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = b;
-  let tint_symbol_2 : i32 = a(1);
-  let r = g(tint_symbol, tint_symbol_1, tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_InBinary) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(i : i32, j : i32, k : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 0;
-  var c = 0;
-  var d = 0;
-  let r = b + g(c, a(0), d) + a(1);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn g(i : i32, j : i32, k : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = 0;
-  var c = 0;
-  var d = 0;
-  let tint_symbol : i32 = b;
-  let tint_symbol_1 : i32 = c;
-  let tint_symbol_2 : i32 = a(0);
-  let tint_symbol_3 : i32 = g(tint_symbol_1, tint_symbol_2, d);
-  let tint_symbol_4 : i32 = a(1);
-  let r = ((tint_symbol + tint_symbol_3) + tint_symbol_4);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_LeftSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  var c = 1;
-  var r = b[a(0)][c];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  var c = 1;
-  let tint_symbol : i32 = a(0);
-  var r = b[tint_symbol][c];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_RightSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  var c = 1;
-  let tint_symbol = c;
-  let tint_symbol_1 = a(0);
-  var r = b[tint_symbol][tint_symbol_1];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  var c = 1;
-  let tint_symbol = c;
-  let tint_symbol_1 = a(0);
-  var r = b[tint_symbol][tint_symbol_1];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_BothSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  var r = b[a(0)][a(1)];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  var r = b[tint_symbol][tint_symbol_1];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_LeftSE_ViaPointerIndex) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  let p = &b;
-  var c = 1;
-  var r = p[a(0)][c];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  let p = &(b);
-  var c = 1;
-  let tint_symbol : i32 = a(0);
-  var r = p[tint_symbol][c];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_RightSE_ViaPointerIndex) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  let p = &b;
-  var c = 1;
-  let tint_symbol = c;
-  let tint_symbol_1 = a(0);
-  var r = p[tint_symbol][tint_symbol_1];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  let p = &(b);
-  var c = 1;
-  let tint_symbol = c;
-  let tint_symbol_1 = a(0);
-  var r = p[tint_symbol][tint_symbol_1];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_2D_BothSE_ViaPointerIndex) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  let p = &b;
-  var r = p[a(0)][a(1)];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  let p = &(b);
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  var r = p[tint_symbol][tint_symbol_1];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToPhony) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  _ = a(0);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  _ = a(0);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToArray1D) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<i32, 10>();
-  b[a(0)] = a(1);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<i32, 10>();
-  let tint_symbol : i32 = a(0);
-  b[tint_symbol] = a(1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToArray2D) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  b[a(0)][a(1)] = a(2);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<i32, 10>, 10>();
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  b[tint_symbol][tint_symbol_1] = a(2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToArray3D) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<array<i32, 10>, 10>, 10>();
-  b[a(0)][a(1)][a(2)] = a(3);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<array<array<i32, 10>, 10>, 10>();
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  let tint_symbol_2 : i32 = a(2);
-  b[tint_symbol][tint_symbol_1][tint_symbol_2] = a(3);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToArray_FromArray) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<i32, 3>();
-  var d = array<array<i32, 3>, 3>();
-  var a_1 = 0;
-  b[a(2)] = d[a(0)][a_1];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = array<i32, 3>();
-  var d = array<array<i32, 3>, 3>();
-  var a_1 = 0;
-  let tint_symbol : i32 = a(2);
-  let tint_symbol_1 : i32 = a(0);
-  b[tint_symbol] = d[tint_symbol_1][a_1];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToVec_BothSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = vec3<i32>();
-  b[a(0)] = a(1);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = vec3<i32>();
-  let tint_symbol : i32 = a(0);
-  b[tint_symbol] = a(1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToVec_LeftSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = vec3<i32>();
-  var c = 0;
-  b[a(0)] = c;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = vec3<i32>();
-  var c = 0;
-  let tint_symbol : i32 = a(0);
-  b[tint_symbol] = c;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Assignment_ToVec_RightSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = vec3<i32>();
-  var c = 0;
-  b[c] = a(0);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var b = vec3<i32>();
-  var c = 0;
-  let tint_symbol : i32 = c;
-  b[tint_symbol] = a(0);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, TypeInitializer_Struct) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-struct S {
-  x : i32,
-  y : i32,
-  z : i32,
-}
-
-fn f() {
-  var r = S(a(0), a(1), a(2));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-struct S {
-  x : i32,
-  y : i32,
-  z : i32,
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  let tint_symbol_2 : i32 = a(2);
-  var r = S(tint_symbol, tint_symbol_1, tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, TypeInitializer_Array1D) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var r = array<i32, 3>(a(0), a(1), a(2));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  let tint_symbol_2 : i32 = a(2);
-  var r = array<i32, 3>(tint_symbol, tint_symbol_1, tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, TypeInitializer_Array2D) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  var r = array<array<i32, 2>, 2>(array<i32, 2>(a(0), a(1)), array<i32, 2>(a(2), a(3)));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 1;
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  let tint_symbol_1 : i32 = a(1);
-  let tint_symbol_2 : array<i32, 2u> = array<i32, 2>(tint_symbol, tint_symbol_1);
-  let tint_symbol_3 : i32 = a(2);
-  let tint_symbol_4 : i32 = a(3);
-  let tint_symbol_5 : array<i32, 2u> = array<i32, 2>(tint_symbol_3, tint_symbol_4);
-  var r = array<array<i32, 2>, 2>(tint_symbol_2, tint_symbol_5);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, MemberAccessor_Vec) {
-    auto* src = R"(
-fn a(i : i32) -> vec3<i32> {
-  return vec3<i32>();
-}
-
-fn f() {
-  var r = a(0).x + a(1).y;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> vec3<i32> {
-  return vec3<i32>();
-}
-
-fn f() {
-  let tint_symbol : vec3<i32> = a(0);
-  let tint_symbol_1 : vec3<i32> = a(1);
-  var r = (tint_symbol.x + tint_symbol_1.y);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, MemberAccessor_Struct) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : i32,
-}
-
-fn a(i : i32) -> S {
-  return S();
-}
-
-fn f() {
-  var r = a(0).x + a(1).y;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  x : i32,
-  y : i32,
-}
-
-fn a(i : i32) -> S {
-  return S();
-}
-
-fn f() {
-  let tint_symbol : S = a(0);
-  let tint_symbol_1 : S = a(1);
-  var r = (tint_symbol.x + tint_symbol_1.y);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, MemberAccessor_Struct_Mixed) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : i32,
-  arr : array<i32, 10>,
-}
-
-fn a(i : i32) -> S {
-  return S();
-}
-
-fn b(i : i32) -> i32 {
-  return 0;
-}
-
-fn f() {
-  var i = 0;
-  var j = 0;
-  var k = 0;
-  var l = 0;
-  var m = 0;
-  var r = a(0).x + i + a(1).y + j + a(2).arr[k + b(3) + l] + m;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  x : i32,
-  y : i32,
-  arr : array<i32, 10>,
-}
-
-fn a(i : i32) -> S {
-  return S();
-}
-
-fn b(i : i32) -> i32 {
-  return 0;
-}
-
-fn f() {
-  var i = 0;
-  var j = 0;
-  var k = 0;
-  var l = 0;
-  var m = 0;
-  let tint_symbol : S = a(0);
-  let tint_symbol_1 : i32 = i;
-  let tint_symbol_2 : S = a(1);
-  let tint_symbol_3 : i32 = j;
-  let tint_symbol_4 : S = a(2);
-  let tint_symbol_5 : i32 = k;
-  let tint_symbol_6 : i32 = b(3);
-  var r = (((((tint_symbol.x + tint_symbol_1) + tint_symbol_2.y) + tint_symbol_3) + tint_symbol_4.arr[((tint_symbol_5 + tint_symbol_6) + l)]) + m);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_Plus_SE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<i32, 10>();
-    let r = v[0] + a(0);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<i32, 10>();
-  let tint_symbol : i32 = v[0];
-  let tint_symbol_1 : i32 = a(0);
-  let r = (tint_symbol + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_Of_SE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<i32, 10>();
-    let r = v[a(0)];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<i32, 10>();
-  let tint_symbol : i32 = a(0);
-  let r = v[tint_symbol];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor2_Of_LeftSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<array<i32, 10>, 10>();
-    let r = v[a(0)][0];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<array<i32, 10>, 10>();
-  let tint_symbol : i32 = a(0);
-  let r = v[tint_symbol][0];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor2_Of_RightSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<array<i32, 10>, 10>();
-    let r = v[0][a(0)];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<array<i32, 10>, 10>();
-  let tint_symbol : i32 = a(0);
-  let r = v[0][tint_symbol];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor2_Of_SEAndVar) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<array<i32, 10>, 10>();
-    var b : i32;
-    let r = v[a(0)][b];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<array<i32, 10>, 10>();
-  var b : i32;
-  let tint_symbol : i32 = a(0);
-  let r = v[tint_symbol][b];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor2_Of_VarAndSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<array<i32, 10>, 10>();
-    var b : i32;
-    let r = v[b][a(0)];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<array<i32, 10>, 10>();
-  var b : i32;
-  let tint_symbol : i32 = b;
-  let tint_symbol_1 : i32 = a(0);
-  let r = v[tint_symbol][tint_symbol_1];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessorOfVar_Plus_SE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<i32, 10>();
-  var b = 0;
-  let r = v[b] + a(0);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<i32, 10>();
-  var b = 0;
-  let tint_symbol : i32 = b;
-  let tint_symbol_1 : i32 = v[tint_symbol];
-  let tint_symbol_2 : i32 = a(0);
-  let r = (tint_symbol_1 + tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessor_Plus_IndexAccessorOfSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<i32, 10>();
-    let r = v[0] + v[a(0)];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<i32, 10>();
-  let tint_symbol : i32 = v[0];
-  let tint_symbol_1 : i32 = a(0);
-  let r = (tint_symbol + v[tint_symbol_1]);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, AssignTo_IndexAccessorOfIndexAccessorOfSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<i32, 10>();
-    var w = array<i32, 10>();
-    v[w[a(0)]] = 1;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<i32, 10>();
-  var w = array<i32, 10>();
-  let tint_symbol : i32 = a(0);
-  v[w[tint_symbol]] = 1;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, AssignTo_IndexAccessorOfIndexAccessorOfLiteralPlusSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<i32, 10>();
-    var w = array<i32, 10>();
-    v[w[0] + a(0)] = 1;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<i32, 10>();
-  var w = array<i32, 10>();
-  let tint_symbol : i32 = w[0];
-  let tint_symbol_1 : i32 = a(0);
-  v[(tint_symbol + tint_symbol_1)] = 1;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest,
-       AssignTo_IndexAccessorOfIndexAccessorOfLiteralPlusIndexAccessorOfSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-    var v = array<i32, 10>();
-    var w = array<i32, 10>();
-    v[w[0] + w[a(0)]] = 1;
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn f() {
-  var v = array<i32, 10>();
-  var w = array<i32, 10>();
-  let tint_symbol : i32 = w[0];
-  let tint_symbol_1 : i32 = a(0);
-  v[(tint_symbol + w[tint_symbol_1])] = 1;
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, IndexAccessorOfLhsSERhsSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn b() -> array<i32, 10> {
-    return array<i32, 10>();
-}
-
-fn f() {
-    let r = b()[a(0)];
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn b() -> array<i32, 10> {
-  return array<i32, 10>();
-}
-
-fn f() {
-  let tint_symbol : array<i32, 10u> = b();
-  let tint_symbol_1 : i32 = a(0);
-  let r = tint_symbol[tint_symbol_1];
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, BinaryIndexAccessorOfLhsSERhsSE) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn b() -> array<i32, 10> {
-    return array<i32, 10>();
-}
-
-fn f() {
-    let r = b()[0] + a(0);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return i;
-}
-
-fn b() -> array<i32, 10> {
-  return array<i32, 10>();
-}
-
-fn f() {
-  let tint_symbol : array<i32, 10u> = b();
-  let tint_symbol_1 : i32 = a(0);
-  let r = (tint_symbol[0] + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, BinaryMemberAccessorPlusSE) {
-    // bclayton@'s example:
-    // https://dawn-review.googlesource.com/c/tint/+/78620/6..8/src/transform/promote_side_effects_to_decl.cc#b490
-    auto* src = R"(
-fn modify_vec(p : ptr<function, vec4<i32>>) -> i32 {
-  (*p).x = 42;
-  return 0;
-}
-
-fn f() {
-  var v = vec4<i32>();
-  let l = v.x + modify_vec(&v);
-  // l should be 0, not 42
-}
-)";
-
-    auto* expect = R"(
-fn modify_vec(p : ptr<function, vec4<i32>>) -> i32 {
-  (*(p)).x = 42;
-  return 0;
-}
-
-fn f() {
-  var v = vec4<i32>();
-  let tint_symbol : i32 = v.x;
-  let tint_symbol_1 : i32 = modify_vec(&(v));
-  let l = (tint_symbol + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, BinaryMemberAccessorPlusSE_ViaPointerDot) {
-    // bclayton@'s example:
-    // https://dawn-review.googlesource.com/c/tint/+/78620/6..8/src/transform/promote_side_effects_to_decl.cc#b490
-    auto* src = R"(
-fn modify_vec(p : ptr<function, vec4<i32>>) -> i32 {
-  (*p).x = 42;
-  return 0;
-}
-
-fn f() {
-  var v = vec4<i32>();
-  let p = &v;
-  let l = p.x + modify_vec(&v);
-  // l should be 0, not 42
-}
-)";
-
-    auto* expect = R"(
-fn modify_vec(p : ptr<function, vec4<i32>>) -> i32 {
-  (*(p)).x = 42;
-  return 0;
-}
-
-fn f() {
-  var v = vec4<i32>();
-  let p = &(v);
-  let tint_symbol : i32 = p.x;
-  let tint_symbol_1 : i32 = modify_vec(&(v));
-  let l = (tint_symbol + tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_ReadOnlyArgAndSE) {
-    // Make sure that read-only args don't get hoisted (tex and samp)
-    auto* src = R"(
-@group(1) @binding(1) var tex: texture_2d_array<u32>;
-@group(1) @binding(2) var samp: sampler;
-@group(1) @binding(2) var arr: binding_array<texture_2d<f32>, 3>;
-
-fn get_uv() -> vec2<f32> {
-  return vec2<f32>(1.0, 2.0);
-}
-
-fn f() {
-  let r = textureGather(1, tex, samp, get_uv(), 1);
-  let p = textureGather(1, arr[0], samp, get_uv());
-}
-)";
-
-    auto* expect = R"(
-@group(1) @binding(1) var tex : texture_2d_array<u32>;
-
-@group(1) @binding(2) var samp : sampler;
-
-@group(1) @binding(2) var arr : binding_array<texture_2d<f32>, 3>;
-
-fn get_uv() -> vec2<f32> {
-  return vec2<f32>(1.0, 2.0);
-}
-
-fn f() {
-  let tint_symbol : vec2<f32> = get_uv();
-  let r = textureGather(1, tex, samp, tint_symbol, 1);
-  let tint_symbol_1 : vec2<f32> = get_uv();
-  let p = textureGather(1, arr[0], samp, tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Call_PtrArgAndSE) {
-    // Make sure that read-only args don't get hoisted (tex and samp)
-    auto* src = R"(
-
-var<private> b : i32 = 0;
-
-fn a(i : i32) -> i32 {
-  b = 42;
-  return 0;
-}
-
-fn g(i : ptr<private, i32>, j : i32) -> i32 {
-  return *i;
-}
-
-fn f() {
-  // a(0) should be hoisted, but not &b
-  let r = g(&b, a(0));
-  // r should be 42
-}
-)";
-
-    auto* expect = R"(
-var<private> b : i32 = 0;
-
-fn a(i : i32) -> i32 {
-  b = 42;
-  return 0;
-}
-
-fn g(i : ptr<private, i32>, j : i32) -> i32 {
-  return *(i);
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  let r = g(&(b), tint_symbol);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, TypeInit_VarPlusI32InitPlusVar) {
-    auto* src = R"(
-fn f() {
-  var b = 0;
-  var c = 0;
-  var d = 0;
-  let r = ((b + i32(c)) + d);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_ArithPlusLogical) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn g(i : bool) -> i32 {
-  return 0;
-}
-
-fn f() {
-  let r = a(0) + g(b(1) && b(2));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn g(i : bool) -> i32 {
-  return 0;
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  var tint_symbol_1 = b(1);
-  if (tint_symbol_1) {
-    tint_symbol_1 = b(2);
-  }
-  let tint_symbol_2 : i32 = g(tint_symbol_1);
-  let r = (tint_symbol + tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_LogicalPlusArith) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn g(i : bool) -> i32 {
-  return 0;
-}
-
-fn f() {
-  let r = g(b(0) && b(1)) + a(2);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn g(i : bool) -> i32 {
-  return 0;
-}
-
-fn f() {
-  var tint_symbol = b(0);
-  if (tint_symbol) {
-    tint_symbol = b(1);
-  }
-  let tint_symbol_1 : i32 = g(tint_symbol);
-  let tint_symbol_2 : i32 = a(2);
-  let r = (tint_symbol_1 + tint_symbol_2);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_ArithAndLogicalArgs) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn g(i : i32, j : bool) -> i32 {
-  return 0;
-}
-
-fn f() {
-  let r = g(a(0), b(1) && b(2));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn g(i : i32, j : bool) -> i32 {
-  return 0;
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  var tint_symbol_1 = b(1);
-  if (tint_symbol_1) {
-    tint_symbol_1 = b(2);
-  }
-  let r = g(tint_symbol, tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_LogicalAndArithArgs) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn g(i : bool, j : i32) -> i32 {
-  return 0;
-}
-
-fn f() {
-  let r = g(b(0) && b(1), a(2));
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn g(i : bool, j : i32) -> i32 {
-  return 0;
-}
-
-fn f() {
-  var tint_symbol = b(0);
-  if (tint_symbol) {
-    tint_symbol = b(1);
-  }
-  let tint_symbol_1 : i32 = a(2);
-  let r = g(tint_symbol, tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Binary_Mixed_Complex) {
-    auto* src = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn c(i : bool) -> i32 {
-  return 0;
-}
-
-fn g(i : bool, j : i32) -> i32 {
-  return 0;
-}
-
-fn f() {
-  let r = a(0) + g(b(1) && b(a(2) + a(3)), a(4)) + a(5);
-}
-)";
-
-    auto* expect = R"(
-fn a(i : i32) -> i32 {
-  return 0;
-}
-
-fn b(i : i32) -> bool {
-  return true;
-}
-
-fn c(i : bool) -> i32 {
-  return 0;
-}
-
-fn g(i : bool, j : i32) -> i32 {
-  return 0;
-}
-
-fn f() {
-  let tint_symbol : i32 = a(0);
-  var tint_symbol_1 = b(1);
-  if (tint_symbol_1) {
-    let tint_symbol_2 : i32 = a(2);
-    let tint_symbol_3 : i32 = a(3);
-    tint_symbol_1 = b((tint_symbol_2 + tint_symbol_3));
-  }
-  let tint_symbol_4 : i32 = a(4);
-  let tint_symbol_5 : i32 = g(tint_symbol_1, tint_symbol_4);
-  let tint_symbol_6 : i32 = a(5);
-  let r = ((tint_symbol + tint_symbol_5) + tint_symbol_6);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, HandleParameter) {
-    auto* src = R"(
-@group(0) @binding(0) var T : texture_2d<f32>;
-@group(0) @binding(1) var S : sampler;
-@group(0) @binding(2) var A : binding_array<texture_2d<f32>, 3>;
-
-var<private> P : vec2<f32>;
-fn side_effects() -> vec2<f32> {
-  P += vec2(1.0);
-  return P;
-}
-
-fn f(t : texture_2d<f32>, s : sampler, a: binding_array<texture_2d<f32>, 3>) {
-  let p = textureSample(t, s, side_effects());
-  let r = textureSample(a[0], s, side_effects());
-}
-
-fn m() {
-  f(T, S, A);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var T : texture_2d<f32>;
-
-@group(0) @binding(1) var S : sampler;
-
-@group(0) @binding(2) var A : binding_array<texture_2d<f32>, 3>;
-
-var<private> P : vec2<f32>;
-
-fn side_effects() -> vec2<f32> {
-  P += vec2(1.0);
-  return P;
-}
-
-fn f(t : texture_2d<f32>, s : sampler, a : binding_array<texture_2d<f32>, 3>) {
-  let tint_symbol : vec2<f32> = side_effects();
-  let p = textureSample(t, s, tint_symbol);
-  let tint_symbol_1 : vec2<f32> = side_effects();
-  let r = textureSample(a[0], s, tint_symbol_1);
-}
-
-fn m() {
-  f(T, S, A);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, BuiltinReturnType) {
-    auto* src = R"(
-fn X(a:vec2f, b:vec2f) {
-}
-
-fn Y() -> vec2f { return vec2f(); }
-
-fn f() {
-    var v: vec2f;
-    X(vec2(), v);   // okay
-    X(vec2(), Y()); // errors
-}
-)";
-
-    auto* expect = R"(
-fn X(a : vec2f, b : vec2f) {
-}
-
-fn Y() -> vec2f {
-  return vec2f();
-}
-
-fn f() {
-  var v : vec2f;
-  X(vec2(), v);
-  let tint_symbol : vec2<f32> = vec2();
-  let tint_symbol_1 : vec2<f32> = Y();
-  X(tint_symbol, tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteSideEffectsToDeclTest, Bug1963) {
-    auto* src = R"(
-fn X(a:vec2f, b:vec2f) {
-}
-
-fn Y() -> vec2f { return vec2f(); }
-
-fn f() {
-    var v: vec2f;
-    X(vec2(), v);   // okay
-    X(vec2(), Y()); // errors
-}
-)";
-
-    auto* expect = R"(
-fn X(a : vec2f, b : vec2f) {
-}
-
-fn Y() -> vec2f {
-  return vec2f();
-}
-
-fn f() {
-  var v : vec2f;
-  X(vec2(), v);
-  let tint_symbol : vec2<f32> = vec2();
-  let tint_symbol_1 : vec2<f32> = Y();
-  X(tint_symbol, tint_symbol_1);
-}
-)";
-
-    auto got = Run<PromoteSideEffectsToDecl>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.cc b/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.cc
deleted file mode 100644
index 1ee9333..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.cc
+++ /dev/null
@@ -1,200 +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/wgsl/ast/transform/remove_continue_in_switch.h"
-
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/continue_statement.h"
-#include "src/tint/lang/wgsl/ast/switch_statement.h"
-#include "src/tint/lang/wgsl/ast/transform/get_insertion_point.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/block_statement.h"
-#include "src/tint/lang/wgsl/sem/loop_statement.h"
-#include "src/tint/lang/wgsl/sem/switch_statement.h"
-#include "src/tint/utils/containers/hashmap.h"
-#include "src/tint/utils/containers/hashset.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::RemoveContinueInSwitch);
-
-namespace tint::ast::transform {
-
-/// PIMPL state for the transform
-struct RemoveContinueInSwitch::State {
-    /// 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() {
-        // First collect all switch statements within loops that contain a continue statement.
-        for (auto* node : src.ASTNodes().Objects()) {
-            auto* cont = node->As<ast::ContinueStatement>();
-            if (!cont) {
-                continue;
-            }
-
-            // If first parent is not a switch within a loop, skip
-            auto* switch_stmt = GetParentSwitchInLoop(sem, cont);
-            if (!switch_stmt) {
-                continue;
-            }
-
-            auto& info = switch_infos.GetOrAdd(switch_stmt, [&] {
-                switch_stmts.Push(switch_stmt);
-                auto* block = sem.Get(switch_stmt)->FindFirstParent<sem::LoopBlockStatement>();
-                return SwitchInfo{/* loop_block */ block, /* continues */ Empty};
-            });
-            info.continues.Push(cont);
-        }
-
-        if (switch_stmts.IsEmpty()) {
-            return SkipTransform;
-        }
-
-        // For each switch statement:
-        // 1. Declare a 'tint_continue' var just before the parent loop, and reset it to false at
-        // the top of the loop body.
-        // 2. Replace 'continue' with 'tint_continue = true; break;'
-        // 3. Insert 'if (tint_continue) { break; }' after switch, and all parent switches, except
-        // for the parent-most, for which we insert 'if (tint_continue) { continue; }'
-        for (auto* switch_stmt : switch_stmts) {
-            const auto& info = switch_infos.Get(switch_stmt);
-
-            auto var_name = loop_to_var.GetOrAdd(info->loop_block, [&] {
-                // Create and insert 'var tint_continue : bool;' before loop
-                auto var = b.Symbols().New("tint_continue");
-                auto* decl = b.Decl(b.Var(var, b.ty.bool_()));
-                auto ip = ast::transform::utils::GetInsertionPoint(
-                    ctx, info->loop_block->Parent()->Declaration());
-                ctx.InsertBefore(ip.first->Declaration()->statements, ip.second, decl);
-
-                // Insert 'tint_continue = false' at top of loop body
-                auto assign_false = b.Assign(var, false);
-                ctx.InsertFront(info->loop_block->Declaration()->statements, assign_false);
-
-                return var;
-            });
-
-            for (auto& c : info->continues) {
-                // Replace 'continue;' with 'tint_continue = true; break;'
-                ctx.Replace(c, b.Assign(b.Expr(var_name), true));
-                ctx.InsertAfter(sem.Get(c)->Block()->Declaration()->statements, c, b.Break());
-            }
-
-            // Insert 'if (tint_continue) { break; }' after switch, and all parent switches,
-            // except for the parent-most, for which we insert 'if (tint_continue) { continue; }'
-            auto* curr_switch = switch_stmt;
-            while (curr_switch) {
-                auto* curr_switch_sem = sem.Get(curr_switch);
-                auto* parent = curr_switch_sem->Parent()->Declaration();
-                auto* next_switch = GetParentSwitchInLoop(sem, parent);
-
-                if (switch_handles_continue.Add(curr_switch)) {
-                    const ast::IfStatement* if_stmt = nullptr;
-                    if (next_switch) {
-                        if_stmt = b.If(b.Expr(var_name), b.Block(b.Break()));
-                    } else {
-                        if_stmt = b.If(b.Expr(var_name), b.Block(b.Continue()));
-                    }
-                    ctx.InsertAfter(curr_switch_sem->Block()->Declaration()->statements,
-                                    curr_switch, if_stmt);
-                }
-
-                curr_switch = next_switch;
-            }
-        }
-
-        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 src.sem
-    const sem::Info& sem = src.Sem();
-
-    // Vector of switch statements within a loop that contains at least one continue statement.
-    Vector<const ast::SwitchStatement*, 4> switch_stmts;
-
-    // Info for each switch statement within a loop that contains at least one continue statement.
-    struct SwitchInfo {
-        // Loop block containing this switch
-        const sem::LoopBlockStatement* loop_block;
-        // Continue statements within this switch
-        Vector<const ast::ContinueStatement*, 4> continues;
-    };
-
-    // Map of switch statements to per-switch info for switch statements within a loop that contains
-    // at least one continue statement.
-    Hashmap<const ast::SwitchStatement*, SwitchInfo, 4> switch_infos;
-
-    // Map of loop block statement to the single 'tint_continue' variable used to replace 'continue'
-    // control flow.
-    Hashmap<const sem::LoopBlockStatement*, Symbol, 4> loop_to_var;
-
-    // Set used to avoid duplicating 'if (tint_continue) { break/continue; }' after each switch
-    // within a loop.
-    Hashset<const ast::SwitchStatement*, 4> switch_handles_continue;
-
-    // If `stmt` is within a switch statement within a loop, returns a pointer to
-    // that switch statement.
-    static const ast::SwitchStatement* GetParentSwitchInLoop(const sem::Info& sem,
-                                                             const ast::Statement* stmt) {
-        // Find whether first parent is a switch or a loop
-        auto* sem_stmt = sem.Get(stmt);
-        auto* sem_parent =
-            sem_stmt->FindFirstParent<sem::SwitchStatement, sem::LoopBlockStatement>();
-
-        if (!sem_parent) {
-            return nullptr;
-        }
-        return sem_parent->Declaration()->As<ast::SwitchStatement>();
-    }
-};
-
-RemoveContinueInSwitch::RemoveContinueInSwitch() = default;
-RemoveContinueInSwitch::~RemoveContinueInSwitch() = default;
-
-ast::transform::Transform::ApplyResult RemoveContinueInSwitch::Apply(
-    const Program& src,
-    const ast::transform::DataMap&,
-    ast::transform::DataMap&) const {
-    State state(src);
-    return state.Run();
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h b/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h
deleted file mode 100644
index 6c62826..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h
+++ /dev/null
@@ -1,60 +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_WGSL_AST_TRANSFORM_REMOVE_CONTINUE_IN_SWITCH_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_CONTINUE_IN_SWITCH_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// This transform replaces continue statements in switch cases with setting a
-/// bool variable, and checking if the variable is set after the switch to
-/// continue. It is necessary to work around various issues including:
-///  * FXC "error X3708: continue cannot be used in a switch". See crbug.com/tint/1080.
-///  * MSL and GLSL invalid code-gen. See crbug.com/tint/2039.
-class RemoveContinueInSwitch final
-    : public Castable<RemoveContinueInSwitch, ast::transform::Transform> {
-  public:
-    /// Constructor
-    RemoveContinueInSwitch();
-
-    /// Destructor
-    ~RemoveContinueInSwitch() 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::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_CONTINUE_IN_SWITCH_H_
diff --git a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_test.cc b/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_test.cc
deleted file mode 100644
index 6f36aab..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_continue_in_switch_test.cc
+++ /dev/null
@@ -1,770 +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/wgsl/ast/transform/remove_continue_in_switch.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using RemoveContinueInSwitchTest = ast::transform::TransformTest;
-
-TEST_F(RemoveContinueInSwitchTest, ShouldRun_True) {
-    auto* src = R"(
-fn f() {
-  var i = 0;
-  loop {
-    switch(i) {
-      case 0: {
-        continue;
-      }
-      default: {
-        break;
-      }
-    }
-    break;
-  }
-}
-)";
-
-    EXPECT_TRUE(ShouldRun<RemoveContinueInSwitch>(src));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ShouldRunEmptyModule_False) {
-    auto* src = "";
-
-    EXPECT_FALSE(ShouldRun<RemoveContinueInSwitch>(src));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ShouldRunContinueNotInSwitch_False) {
-    auto* src = R"(
-fn f() {
-  var i = 0;
-  loop {
-    switch(i) {
-      case 0: {
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-
-    if (true) {
-      continue;
-    }
-    break;
-  }
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<RemoveContinueInSwitch>(src));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ShouldRunContinueInLoopInSwitch_False) {
-    auto* src = R"(
-fn f() {
-  var i = 0;
-  switch(i) {
-    case 0: {
-      loop {
-        if (true) {
-          continue;
-        }
-        break;
-      }
-      break;
-    }
-    default: {
-      break;
-    }
-  }
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<RemoveContinueInSwitch>(src));
-}
-
-TEST_F(RemoveContinueInSwitchTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = src;
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, SingleContinue) {
-    auto* src = R"(
-fn f() {
-  var i = 0;
-  loop {
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        continue;
-      }
-      default: {
-        break;
-      }
-    }
-    let marker2 = 0;
-    break;
-
-    continuing {
-      let marker3 = 0;
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var i = 0;
-  var tint_continue : bool;
-  loop {
-    tint_continue = false;
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        tint_continue = true;
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    if (tint_continue) {
-      continue;
-    }
-    let marker2 = 0;
-    break;
-
-    continuing {
-      let marker3 = 0;
-    }
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, MultipleContinues) {
-    auto* src = R"(
-fn f() {
-  var i = 0;
-  loop {
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        continue;
-      }
-      case 1: {
-        continue;
-      }
-      case 2: {
-        continue;
-      }
-      default: {
-        break;
-      }
-    }
-    let marker2 = 0;
-    break;
-
-    continuing {
-      let marker3 = 0;
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var i = 0;
-  var tint_continue : bool;
-  loop {
-    tint_continue = false;
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        tint_continue = true;
-        break;
-      }
-      case 1: {
-        tint_continue = true;
-        break;
-      }
-      case 2: {
-        tint_continue = true;
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    if (tint_continue) {
-      continue;
-    }
-    let marker2 = 0;
-    break;
-
-    continuing {
-      let marker3 = 0;
-    }
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, MultipleSwitch) {
-    auto* src = R"(
-fn f() {
-  var i = 0;
-  loop {
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        continue;
-      }
-      default: {
-        break;
-      }
-    }
-    let marker2 = 0;
-
-    let marker3 = 0;
-    switch(i) {
-      case 0: {
-        continue;
-      }
-      default: {
-        break;
-      }
-    }
-    let marker4 = 0;
-
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var i = 0;
-  var tint_continue : bool;
-  loop {
-    tint_continue = false;
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        tint_continue = true;
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    if (tint_continue) {
-      continue;
-    }
-    let marker2 = 0;
-    let marker3 = 0;
-    switch(i) {
-      case 0: {
-        tint_continue = true;
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    if (tint_continue) {
-      continue;
-    }
-    let marker4 = 0;
-    break;
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, NestedLoopSwitchSwitch) {
-    auto* src = R"(
-fn f() {
-  var j = 0;
-  for (var i = 0; i < 2; i += 2) {
-    switch(i) {
-      case 0: {
-        switch(j) {
-          case 0: {
-            continue;
-          }
-          default: {
-          }
-        }
-      }
-      default: {
-      }
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var j = 0;
-  var tint_continue : bool;
-  for(var i = 0; (i < 2); i += 2) {
-    tint_continue = false;
-    switch(i) {
-      case 0: {
-        switch(j) {
-          case 0: {
-            tint_continue = true;
-            break;
-          }
-          default: {
-          }
-        }
-        if (tint_continue) {
-          break;
-        }
-      }
-      default: {
-      }
-    }
-    if (tint_continue) {
-      continue;
-    }
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, NestedLoopLoopSwitch) {
-    auto* src = R"(
-fn f() {
-  for (var i = 0; i < 2; i += 2) {
-    for (var j = 0; j < 2; j += 2) {
-      switch(i) {
-        case 0: {
-          continue;
-        }
-        default: {
-        }
-      }
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  for(var i = 0; (i < 2); i += 2) {
-    var tint_continue : bool;
-    for(var j = 0; (j < 2); j += 2) {
-      tint_continue = false;
-      switch(i) {
-        case 0: {
-          tint_continue = true;
-          break;
-        }
-        default: {
-        }
-      }
-      if (tint_continue) {
-        continue;
-      }
-    }
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, NestedLoopSwitchLoopSwitch) {
-    auto* src = R"(
-fn f() {
-  for (var i = 0; i < 2; i += 2) {
-    switch(i) {
-      case 0: {
-        for (var j = 0; j < 2; j += 2) {
-          switch(j) {
-            case 0: {
-              continue; // j loop
-            }
-            default: {
-            }
-          }
-        }
-        continue; // i loop
-      }
-      default: {
-      }
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var tint_continue_1 : bool;
-  for(var i = 0; (i < 2); i += 2) {
-    tint_continue_1 = false;
-    switch(i) {
-      case 0: {
-        var tint_continue : bool;
-        for(var j = 0; (j < 2); j += 2) {
-          tint_continue = false;
-          switch(j) {
-            case 0: {
-              tint_continue = true;
-              break;
-            }
-            default: {
-            }
-          }
-          if (tint_continue) {
-            continue;
-          }
-        }
-        tint_continue_1 = true;
-        break;
-      }
-      default: {
-      }
-    }
-    if (tint_continue_1) {
-      continue;
-    }
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, NestedLoopSwitchLoopSwitchSwitch) {
-    auto* src = R"(
-fn f() {
-  var k = 0;
-  for (var i = 0; i < 2; i += 2) {
-    switch(i) {
-      case 0: {
-        for (var j = 0; j < 2; j += 2) {
-          switch(j) {
-            case 0: {
-              continue; // j loop
-            }
-            case 1: {
-              switch (k) {
-                case 0: {
-                  continue; // j loop
-                }
-                default: {
-                }
-              }
-            }
-            default: {
-            }
-          }
-        }
-        continue; // i loop
-      }
-      default: {
-      }
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var k = 0;
-  var tint_continue_1 : bool;
-  for(var i = 0; (i < 2); i += 2) {
-    tint_continue_1 = false;
-    switch(i) {
-      case 0: {
-        var tint_continue : bool;
-        for(var j = 0; (j < 2); j += 2) {
-          tint_continue = false;
-          switch(j) {
-            case 0: {
-              tint_continue = true;
-              break;
-            }
-            case 1: {
-              switch(k) {
-                case 0: {
-                  tint_continue = true;
-                  break;
-                }
-                default: {
-                }
-              }
-              if (tint_continue) {
-                break;
-              }
-            }
-            default: {
-            }
-          }
-          if (tint_continue) {
-            continue;
-          }
-        }
-        tint_continue_1 = true;
-        break;
-      }
-      default: {
-      }
-    }
-    if (tint_continue_1) {
-      continue;
-    }
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ExtraScopes) {
-    auto* src = R"(
-fn f() {
-  var i = 0;
-  var a = true;
-  var b = true;
-  var c = true;
-  var d = true;
-  loop {
-    if (a) {
-      if (b) {
-        let marker1 = 0;
-        switch(i) {
-          case 0: {
-            if (c) {
-              if (d) {
-                continue;
-              }
-            }
-            break;
-          }
-          default: {
-            break;
-          }
-        }
-        let marker2 = 0;
-        break;
-      }
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var i = 0;
-  var a = true;
-  var b = true;
-  var c = true;
-  var d = true;
-  var tint_continue : bool;
-  loop {
-    tint_continue = false;
-    if (a) {
-      if (b) {
-        let marker1 = 0;
-        switch(i) {
-          case 0: {
-            if (c) {
-              if (d) {
-                tint_continue = true;
-                break;
-              }
-            }
-            break;
-          }
-          default: {
-            break;
-          }
-        }
-        if (tint_continue) {
-          continue;
-        }
-        let marker2 = 0;
-        break;
-      }
-    }
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, ForLoop) {
-    auto* src = R"(
-fn f() {
-  for (var i = 0; i < 4; i = i + 1) {
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        continue;
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    let marker2 = 0;
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var tint_continue : bool;
-  for(var i = 0; (i < 4); i = (i + 1)) {
-    tint_continue = false;
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        tint_continue = true;
-        break;
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    if (tint_continue) {
-      continue;
-    }
-    let marker2 = 0;
-    break;
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemoveContinueInSwitchTest, While) {
-    auto* src = R"(
-fn f() {
-  var i = 0;
-  while (i < 4) {
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        continue;
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    let marker2 = 0;
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var i = 0;
-  var tint_continue : bool;
-  while((i < 4)) {
-    tint_continue = false;
-    let marker1 = 0;
-    switch(i) {
-      case 0: {
-        tint_continue = true;
-        break;
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    if (tint_continue) {
-      continue;
-    }
-    let marker2 = 0;
-    break;
-  }
-}
-)";
-
-    ast::transform::DataMap data;
-    auto got = Run<RemoveContinueInSwitch>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/remove_phonies.cc b/src/tint/lang/wgsl/ast/transform/remove_phonies.cc
deleted file mode 100644
index 2994e09..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_phonies.cc
+++ /dev/null
@@ -1,172 +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/wgsl/ast/transform/remove_phonies.h"
-
-#include <memory>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/core/evaluation_stage.h"
-#include "src/tint/lang/wgsl/ast/traverse_expressions.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/block_statement.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::RemovePhonies);
-
-namespace tint::ast::transform {
-namespace {
-
-using SinkSignature = std::vector<const core::type::Type*>;
-
-}  // namespace
-
-RemovePhonies::RemovePhonies() = default;
-
-RemovePhonies::~RemovePhonies() = default;
-
-Transform::ApplyResult RemovePhonies::Apply(const Program& src, const DataMap&, DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    auto& sem = src.Sem();
-
-    Hashmap<SinkSignature, Symbol, 8> sinks;
-
-    bool made_changes = false;
-    for (auto* node : src.ASTNodes().Objects()) {
-        Switch(
-            node,
-            [&](const AssignmentStatement* stmt) {
-                if (stmt->lhs->Is<PhonyExpression>()) {
-                    made_changes = true;
-
-                    std::vector<const Expression*> side_effects;
-                    if (!TraverseExpressions(stmt->rhs, [&](const CallExpression* expr) {
-                            // CallExpression may map to a function or builtin call
-                            // (both may have side-effects), or a value constructor or value
-                            // conversion (both do not have side effects).
-                            auto* call = sem.Get<sem::Call>(expr);
-                            if (!call) {
-                                // Semantic node must be a Materialize, in which case the
-                                // expression was creation-time (compile time), so could not
-                                // have side effects. Just skip.
-                                return TraverseAction::Skip;
-                            }
-                            if (call->Target()->IsAnyOf<sem::Function, sem::BuiltinFn>() &&
-                                call->HasSideEffects() &&
-                                call->Stage() != core::EvaluationStage::kNotEvaluated) {
-                                side_effects.push_back(expr);
-                                return TraverseAction::Skip;
-                            }
-                            return TraverseAction::Descend;
-                        })) {
-                        return;
-                    }
-
-                    if (side_effects.empty()) {
-                        // Phony assignment with no side effects.
-                        // Just remove it.
-                        RemoveStatement(ctx, stmt);
-                        return;
-                    }
-
-                    if (side_effects.size() == 1) {
-                        if (auto* call_expr = side_effects[0]->As<CallExpression>()) {
-                            // Phony assignment with single call side effect.
-                            auto* call = sem.Get(call_expr)->Unwrap()->As<sem::Call>();
-                            if (call->Target()->MustUse()) {
-                                // Replace phony assignment assignment to uniquely named let.
-                                ctx.Replace<Statement>(stmt, [&, call_expr] {  //
-                                    auto name = b.Symbols().New("tint_phony");
-                                    auto* rhs = ctx.Clone(call_expr);
-                                    return b.Decl(b.Let(name, rhs));
-                                });
-                            } else {
-                                // Replace phony assignment with call statement.
-                                ctx.Replace(stmt, [&, call_expr] {  //
-                                    return b.CallStmt(ctx.Clone(call_expr));
-                                });
-                            }
-                            return;
-                        }
-                    }
-
-                    // Phony assignment with multiple side effects.
-                    // Generate a call to a placeholder function with the side
-                    // effects as arguments.
-                    ctx.Replace(stmt, [&, side_effects] {
-                        SinkSignature sig;
-                        for (auto* arg : side_effects) {
-                            sig.push_back(sem.GetVal(arg)->Type()->UnwrapRef());
-                        }
-                        auto sink = sinks.GetOrAdd(sig, [&] {
-                            auto name = b.Symbols().New("phony_sink");
-                            tint::Vector<const Parameter*, 8> params;
-                            for (auto* ty : sig) {
-                                auto ast_ty = CreateASTTypeFor(ctx, ty);
-                                params.Push(b.Param("p" + std::to_string(params.Length()), ast_ty));
-                            }
-                            b.Func(name, params, b.ty.void_(), {});
-                            return name;
-                        });
-                        tint::Vector<const Expression*, 8> args;
-                        for (auto* arg : side_effects) {
-                            args.Push(ctx.Clone(arg));
-                        }
-                        return b.CallStmt(b.Call(sink, args));
-                    });
-                }
-            },
-            [&](const CallStatement* stmt) {
-                // Remove call statements to const value-returning functions.
-                // TODO(crbug.com/tint/1637): Remove if `stmt->expr` has no side-effects.
-                auto* sem_expr = sem.Get(stmt->expr);
-                if ((sem_expr->ConstantValue() != nullptr) && !sem_expr->HasSideEffects()) {
-                    made_changes = true;
-                    ctx.Remove(sem.Get(stmt)->Block()->Declaration()->statements, stmt);
-                }
-            });
-    }
-
-    if (!made_changes) {
-        return SkipTransform;
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/remove_phonies.h b/src/tint/lang/wgsl/ast/transform/remove_phonies.h
deleted file mode 100644
index 665dc7f..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_phonies.h
+++ /dev/null
@@ -1,59 +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_WGSL_AST_TRANSFORM_REMOVE_PHONIES_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_PHONIES_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// RemovePhonies is a Transform that removes all phony-assignment statements,
-/// while preserving function call expressions in the RHS of the assignment that
-/// may have side-effects. It also removes calls to builtins that return a constant value.
-/// @note RemovePhonies must be run after the PromoteSideEffectsToDecl transform, otherwise `f` in
-/// `_ = cond && f()` may get hoisted to a call statement without the short-circuiting conditional.
-class RemovePhonies final : public Castable<RemovePhonies, Transform> {
-  public:
-    /// Constructor
-    RemovePhonies();
-
-    /// Destructor
-    ~RemovePhonies() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_REMOVE_PHONIES_H_
diff --git a/src/tint/lang/wgsl/ast/transform/remove_phonies_test.cc b/src/tint/lang/wgsl/ast/transform/remove_phonies_test.cc
deleted file mode 100644
index dc1dbcd..0000000
--- a/src/tint/lang/wgsl/ast/transform/remove_phonies_test.cc
+++ /dev/null
@@ -1,525 +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/wgsl/ast/transform/remove_phonies.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using RemovePhoniesTest = TransformTest;
-
-TEST_F(RemovePhoniesTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<RemovePhonies>(src));
-}
-
-TEST_F(RemovePhoniesTest, ShouldRunHasPhony) {
-    auto* src = R"(
-fn f() {
-  _ = 1;
-}
-)";
-
-    EXPECT_TRUE(ShouldRun<RemovePhonies>(src));
-}
-
-TEST_F(RemovePhoniesTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = "";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, NoSideEffects) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn f() {
-  var v : i32;
-  _ = &v;
-  _ = 1;
-  _ = 1 + 2;
-  _ = t;
-  _ = u32(3.0);
-  _ = f32(i32(4u));
-  _ = vec2<f32>(5.0);
-  _ = vec3<i32>(6, 7, 8);
-  _ = mat2x2<f32>(9.0, 10.0, 11.0, 12.0);
-  _ = atan2(1.0, 2.0);
-  _ = clamp(1.0, 2.0, 3.0);
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn f() {
-  var v : i32;
-}
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, SingleSideEffects) {
-    auto* src = R"(
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn f() {
-  _ = neg(1);
-  _ = add(2, 3);
-  _ = add(neg(4), neg(5));
-  _ = u32(neg(6));
-  _ = f32(add(7, 8));
-  _ = vec2<f32>(f32(neg(9)));
-  _ = vec3<i32>(1, neg(10), 3);
-  _ = mat2x2<f32>(1.0, f32(add(11, 12)), 3.0, 4.0);
-}
-)";
-
-    auto* expect = R"(
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn f() {
-  neg(1);
-  add(2, 3);
-  add(neg(4), neg(5));
-  neg(6);
-  add(7, 8);
-  neg(9);
-  neg(10);
-  add(11, 12);
-}
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, SingleSideEffectsMustUse) {
-    auto* src = R"(
-@must_use
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-
-@must_use
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn f() {
-  let tint_phony = 42;
-  _ = neg(1);
-  _ = 100 + add(2, 3) + 200;
-  _ = add(neg(4), neg(5)) + 6;
-  _ = u32(neg(6));
-  _ = f32(add(7, 8));
-  _ = vec2<f32>(f32(neg(9)));
-  _ = vec3<i32>(1, neg(10), 3);
-  _ = mat2x2<f32>(1.0, f32(add(11, 12)), 3.0, 4.0);
-}
-)";
-
-    auto* expect = R"(
-@must_use
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-
-@must_use
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn f() {
-  let tint_phony = 42;
-  let tint_phony_1 = neg(1);
-  let tint_phony_2 = add(2, 3);
-  let tint_phony_3 = add(neg(4), neg(5));
-  let tint_phony_4 = neg(6);
-  let tint_phony_5 = add(7, 8);
-  let tint_phony_6 = neg(9);
-  let tint_phony_7 = neg(10);
-  let tint_phony_8 = add(11, 12);
-}
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, SingleSideEffects_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  _ = neg(1);
-  _ = add(2, 3);
-  _ = add(neg(4), neg(5));
-  _ = u32(neg(6));
-  _ = f32(add(7, 8));
-  _ = vec2<f32>(f32(neg(9)));
-  _ = vec3<i32>(1, neg(10), 3);
-  _ = mat2x2<f32>(1.0, f32(add(11, 12)), 3.0, 4.0);
-}
-
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  neg(1);
-  add(2, 3);
-  add(neg(4), neg(5));
-  neg(6);
-  add(7, 8);
-  neg(9);
-  neg(10);
-  add(11, 12);
-}
-
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, MultipleSideEffects) {
-    auto* src = R"(
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-
-@must_use
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn xor(a : u32, b : u32) -> u32 {
-  return (a ^ b);
-}
-
-fn f() {
-  _ = (1 + add(2 + add(3, 4), 5)) * add(6, 7) * neg(8);
-  _ = add(9, neg(10)) + neg(11);
-  _ = xor(12u, 13u) + xor(14u, 15u);
-  _ = neg(16) / neg(17) + add(18, 19);
-}
-)";
-
-    auto* expect = R"(
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-
-@must_use
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn xor(a : u32, b : u32) -> u32 {
-  return (a ^ b);
-}
-
-fn phony_sink(p0 : i32, p1 : i32, p2 : i32) {
-}
-
-fn phony_sink_1(p0 : i32, p1 : i32) {
-}
-
-fn phony_sink_2(p0 : u32, p1 : u32) {
-}
-
-fn f() {
-  phony_sink(add((2 + add(3, 4)), 5), add(6, 7), neg(8));
-  phony_sink_1(add(9, neg(10)), neg(11));
-  phony_sink_2(xor(12u, 13u), xor(14u, 15u));
-  phony_sink(neg(16), neg(17), add(18, 19));
-}
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, MultipleSideEffects_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  _ = (1 + add(2 + add(3, 4), 5)) * add(6, 7) * neg(8);
-  _ = add(9, neg(10)) + neg(11);
-  _ = xor(12u, 13u) + xor(14u, 15u);
-  _ = neg(16) / neg(17) + add(18, 19);
-}
-
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn xor(a : u32, b : u32) -> u32 {
-  return (a ^ b);
-}
-)";
-
-    auto* expect = R"(
-fn phony_sink(p0 : i32, p1 : i32, p2 : i32) {
-}
-
-fn phony_sink_1(p0 : i32, p1 : i32) {
-}
-
-fn phony_sink_2(p0 : u32, p1 : u32) {
-}
-
-fn f() {
-  phony_sink(add((2 + add(3, 4)), 5), add(6, 7), neg(8));
-  phony_sink_1(add(9, neg(10)), neg(11));
-  phony_sink_2(xor(12u, 13u), xor(14u, 15u));
-  phony_sink(neg(16), neg(17), add(18, 19));
-}
-
-fn neg(a : i32) -> i32 {
-  return -(a);
-}
-
-fn add(a : i32, b : i32) -> i32 {
-  return (a + b);
-}
-
-fn xor(a : u32, b : u32) -> u32 {
-  return (a ^ b);
-}
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, ForLoop) {
-    auto* src = R"(
-struct S {
-  arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-
-fn x() -> i32 {
-  return 0;
-}
-
-fn y() -> i32 {
-  return 0;
-}
-
-fn z() -> i32 {
-  return 0;
-}
-
-fn f() {
-  for (_ = &s.arr; ;_ = &s.arr) {
-    break;
-  }
-  for (_ = x(); ;_ = y() + z()) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-
-fn x() -> i32 {
-  return 0;
-}
-
-fn y() -> i32 {
-  return 0;
-}
-
-fn z() -> i32 {
-  return 0;
-}
-
-fn phony_sink(p0 : i32, p1 : i32) {
-}
-
-fn f() {
-  for(; ; ) {
-    break;
-  }
-  for(x(); ; phony_sink(y(), z())) {
-    break;
-  }
-}
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, ForLoop_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  for (_ = &s.arr; ;_ = &s.arr) {
-    break;
-  }
-  for (_ = x(); ;_ = y() + z()) {
-    break;
-  }
-}
-
-fn x() -> i32 {
-  return 0;
-}
-
-fn y() -> i32 {
-  return 0;
-}
-
-fn z() -> i32 {
-  return 0;
-}
-
-struct S {
-  arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-)";
-
-    auto* expect = R"(
-fn phony_sink(p0 : i32, p1 : i32) {
-}
-
-fn f() {
-  for(; ; ) {
-    break;
-  }
-  for(x(); ; phony_sink(y(), z())) {
-    break;
-  }
-}
-
-fn x() -> i32 {
-  return 0;
-}
-
-fn y() -> i32 {
-  return 0;
-}
-
-fn z() -> i32 {
-  return 0;
-}
-
-struct S {
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> s : S;
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RemovePhoniesTest, ConstShortCircuit) {
-    auto* src = R"(
-fn a(v : i32) -> i32 {
-  return v;
-}
-
-fn b() {
-  _ = false && (a(4294967295) < a(a(4294967295)));
-}
-)";
-
-    auto* expect = R"(
-fn a(v : i32) -> i32 {
-  return v;
-}
-
-fn b() {
-}
-)";
-
-    auto got = Run<RemovePhonies>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/renamer.cc b/src/tint/lang/wgsl/ast/transform/renamer.cc
deleted file mode 100644
index a329488..0000000
--- a/src/tint/lang/wgsl/ast/transform/renamer.cc
+++ /dev/null
@@ -1,1421 +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/wgsl/ast/transform/renamer.h"
-
-#include <memory>
-#include <utility>
-
-#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/builtin_enum_expression.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/type_expression.h"
-#include "src/tint/lang/wgsl/sem/value_constructor.h"
-#include "src/tint/lang/wgsl/sem/value_conversion.h"
-#include "src/tint/utils/rtti/switch.h"
-#include "src/tint/utils/text/unicode.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Renamer);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Renamer::Config);
-
-namespace tint::ast::transform {
-
-namespace {
-
-// This list is used for a binary search and must be kept in sorted order.
-static constexpr std::array<const char*, 358> kReservedKeywordsGLSL = {
-    "abs",
-    "acos",
-    "acosh",
-    "active",
-    "all",
-    "any",
-    "asin",
-    "asinh",
-    "asm",
-    "atan",
-    "atanh",
-    "atomicAdd",
-    "atomicAnd",
-    "atomicCompSwap",
-    "atomicCounter",
-    "atomicCounterDecrement",
-    "atomicCounterIncrement",
-    "atomicExchange",
-    "atomicMax",
-    "atomicMin",
-    "atomicOr",
-    "atomicXor",
-    "atomic_uint",
-    "attribute",
-    "barrier",
-    "bitCount",
-    "bitfieldExtract",
-    "bitfieldInsert",
-    "bitfieldReverse",
-    "bool",
-    "break",
-    "buffer",
-    "bvec2",
-    "bvec3",
-    "bvec4",
-    "case",
-    "cast",
-    "ceil",
-    "centroid",
-    "clamp",
-    "class",
-    "coherent",
-    "common",
-    "const",
-    "continue",
-    "cos",
-    "cosh",
-    "cross",
-    "dFdx",
-    "dFdy",
-    "default",
-    "degrees",
-    "determinant",
-    "discard",
-    "distance",
-    "dmat2",
-    "dmat2x2",
-    "dmat2x3",
-    "dmat2x4",
-    "dmat3",
-    "dmat3x2",
-    "dmat3x3",
-    "dmat3x4",
-    "dmat4",
-    "dmat4x2",
-    "dmat4x3",
-    "dmat4x4",
-    "do",
-    "dot",
-    "double",
-    "dvec2",
-    "dvec3",
-    "dvec4",
-    "else",
-    "enum",
-    "equal",
-    "exp",
-    "exp2",
-    "extern",
-    "external",
-    "faceforward",
-    "false",
-    "filter",
-    "findLSB",
-    "findMSB",
-    "fixed",
-    "flat",
-    "float",
-    "floatBitsToInt",
-    "floatBitsToUint",
-    "floor",
-    "for",
-    "fract",
-    "frexp",
-    "fvec2",
-    "fvec3",
-    "fvec4",
-    "fwidth",
-    "gl_BaseInstance",
-    "gl_BaseVertex",
-    "gl_ClipDistance",
-    "gl_DepthRangeParameters",
-    "gl_DrawID",
-    "gl_FragCoord",
-    "gl_FragDepth",
-    "gl_FrontFacing",
-    "gl_GlobalInvocationID",
-    "gl_InstanceID",
-    "gl_LocalInvocationID",
-    "gl_LocalInvocationIndex",
-    "gl_NumSamples",
-    "gl_NumWorkGroups",
-    "gl_PerVertex",
-    "gl_PointCoord",
-    "gl_PointSize",
-    "gl_Position",
-    "gl_PrimitiveID",
-    "gl_SampleID",
-    "gl_SampleMask",
-    "gl_SampleMaskIn",
-    "gl_SamplePosition",
-    "gl_VertexID",
-    "gl_WorkGroupID",
-    "gl_WorkGroupSize",
-    "goto",
-    "greaterThan",
-    "greaterThanEqual",
-    "groupMemoryBarrier",
-    "half",
-    "highp",
-    "hvec2",
-    "hvec3",
-    "hvec4",
-    "if",
-    "iimage1D",
-    "iimage1DArray",
-    "iimage2D",
-    "iimage2DArray",
-    "iimage2DMS",
-    "iimage2DMSArray",
-    "iimage2DRect",
-    "iimage3D",
-    "iimageBuffer",
-    "iimageCube",
-    "iimageCubeArray",
-    "image1D",
-    "image1DArray",
-    "image2D",
-    "image2DArray",
-    "image2DMS",
-    "image2DMSArray",
-    "image2DRect",
-    "image3D",
-    "imageBuffer",
-    "imageCube",
-    "imageCubeArray",
-    "imageLoad",
-    "imageSize",
-    "imageStore",
-    "imulExtended",
-    "in",
-    "inline",
-    "inout",
-    "input",
-    "int",
-    "intBitsToFloat",
-    "interface",
-    "invariant",
-    "inverse",
-    "inversesqrt",
-    "isampler1D",
-    "isampler1DArray",
-    "isampler2D",
-    "isampler2DArray",
-    "isampler2DMS",
-    "isampler2DMSArray",
-    "isampler2DRect",
-    "isampler3D",
-    "isamplerBuffer",
-    "isamplerCube",
-    "isamplerCubeArray",
-    "isinf",
-    "isnan",
-    "ivec2",
-    "ivec3",
-    "ivec4",
-    "layout",
-    "ldexp",
-    "length",
-    "lessThan",
-    "lessThanEqual",
-    "log",
-    "log2",
-    "long",
-    "lowp",
-    "main",
-    "mat2",
-    "mat2x2",
-    "mat2x3",
-    "mat2x4",
-    "mat3",
-    "mat3x2",
-    "mat3x3",
-    "mat3x4",
-    "mat4",
-    "mat4x2",
-    "mat4x3",
-    "mat4x4",
-    "matrixCompMult",
-    "max",
-    "mediump",
-    "memoryBarrier",
-    "memoryBarrierAtomicCounter",
-    "memoryBarrierBuffer",
-    "memoryBarrierImage",
-    "memoryBarrierShared",
-    "min",
-    "mix",
-    "mod",
-    "modf",
-    "namespace",
-    "noinline",
-    "non_coherent",
-    "noncoherent",
-    "noperspective",
-    "normalize",
-    "not",
-    "notEqual",
-    "out",
-    "outerProduct",
-    "output",
-    "packHalf2x16",
-    "packSnorm2x16",
-    "packSnorm4x8",
-    "packUnorm2x16",
-    "packUnorm4x8",
-    "partition",
-    "patch",
-    "pow",
-    "precise",
-    "precision",
-    "public",
-    "radians",
-    "readonly",
-    "reflect",
-    "refract",
-    "resource",
-    "restrict",
-    "return",
-    "round",
-    "roundEven",
-    "sample",
-    "sampler1D",
-    "sampler1DArray",
-    "sampler1DArrayShadow",
-    "sampler1DShadow",
-    "sampler2D",
-    "sampler2DArray",
-    "sampler2DArrayShadow",
-    "sampler2DMS",
-    "sampler2DMSArray",
-    "sampler2DRect",
-    "sampler2DRectShadow",
-    "sampler2DShadow",
-    "sampler3D",
-    "sampler3DRect",
-    "samplerBuffer",
-    "samplerCube",
-    "samplerCubeArray",
-    "samplerCubeArrayShadow",
-    "samplerCubeShadow",
-    "shared",
-    "short",
-    "sign",
-    "sin",
-    "sinh",
-    "sizeof",
-    "smooth",
-    "smoothstep",
-    "sqrt",
-    "static",
-    "step",
-    "struct",
-    "subroutine",
-    "superp",
-    "switch",
-    "tan",
-    "tanh",
-    "template",
-    "texelFetch",
-    "texelFetchOffset",
-    "texture",
-    "textureGather",
-    "textureGatherOffset",
-    "textureGrad",
-    "textureGradOffset",
-    "textureLod",
-    "textureLodOffset",
-    "textureOffset",
-    "textureProj",
-    "textureProjGrad",
-    "textureProjGradOffset",
-    "textureProjLod",
-    "textureProjLodOffset",
-    "textureProjOffset",
-    "textureSize",
-    "this",
-    "transpose",
-    "true",
-    "trunc",
-    "typedef",
-    "uaddCarry",
-    "uimage1D",
-    "uimage1DArray",
-    "uimage2D",
-    "uimage2DArray",
-    "uimage2DMS",
-    "uimage2DMSArray",
-    "uimage2DRect",
-    "uimage3D",
-    "uimageBuffer",
-    "uimageCube",
-    "uimageCubeArray",
-    "uint",
-    "uintBitsToFloat",
-    "umulExtended",
-    "uniform",
-    "union",
-    "unpackHalf2x16",
-    "unpackSnorm2x16",
-    "unpackSnorm4x8",
-    "unpackUnorm2x16",
-    "unpackUnorm4x8",
-    "unsigned",
-    "usampler1D",
-    "usampler1DArray",
-    "usampler2D",
-    "usampler2DArray",
-    "usampler2DMS",
-    "usampler2DMSArray",
-    "usampler2DRect",
-    "usampler3D",
-    "usamplerBuffer",
-    "usamplerCube",
-    "usamplerCubeArray",
-    "using",
-    "usubBorrow",
-    "uvec2",
-    "uvec3",
-    "uvec4",
-    "varying",
-    "vec2",
-    "vec3",
-    "vec4",
-    "void",
-    "volatile",
-    "while",
-    "writeonly",
-};
-
-// This list is used for a binary search and must be kept in sorted order.
-static constexpr std::array<const char*, 570> kReservedKeywordsHLSL = {
-    "AddressU",
-    "AddressV",
-    "AddressW",
-    "AllMemoryBarrier",
-    "AllMemoryBarrierWithGroupSync",
-    "AppendStructuredBuffer",
-    "BINORMAL",
-    "BLENDINDICES",
-    "BLENDWEIGHT",
-    "BlendState",
-    "BorderColor",
-    "Buffer",
-    "ByteAddressBuffer",
-    "COLOR",
-    "CheckAccessFullyMapped",
-    "ComparisonFunc",
-    "CompileShader",
-    "ComputeShader",
-    "ConsumeStructuredBuffer",
-    "D3DCOLORtoUBYTE4",
-    "DEPTH",
-    "DepthStencilState",
-    "DepthStencilView",
-    "DeviceMemoryBarrier",
-    "DeviceMemroyBarrierWithGroupSync",
-    "DomainShader",
-    "EvaluateAttributeAtCentroid",
-    "EvaluateAttributeAtSample",
-    "EvaluateAttributeSnapped",
-    "FOG",
-    "Filter",
-    "GeometryShader",
-    "GetRenderTargetSampleCount",
-    "GetRenderTargetSamplePosition",
-    "GroupMemoryBarrier",
-    "GroupMemroyBarrierWithGroupSync",
-    "Hullshader",
-    "InputPatch",
-    "InterlockedAdd",
-    "InterlockedAnd",
-    "InterlockedCompareExchange",
-    "InterlockedCompareStore",
-    "InterlockedExchange",
-    "InterlockedMax",
-    "InterlockedMin",
-    "InterlockedOr",
-    "InterlockedXor",
-    "LineStream",
-    "MaxAnisotropy",
-    "MaxLOD",
-    "MinLOD",
-    "MipLODBias",
-    "NORMAL",
-    "NULL",
-    "Normal",
-    "OutputPatch",
-    "POSITION",
-    "POSITIONT",
-    "PSIZE",
-    "PixelShader",
-    "PointStream",
-    "Process2DQuadTessFactorsAvg",
-    "Process2DQuadTessFactorsMax",
-    "Process2DQuadTessFactorsMin",
-    "ProcessIsolineTessFactors",
-    "ProcessQuadTessFactorsAvg",
-    "ProcessQuadTessFactorsMax",
-    "ProcessQuadTessFactorsMin",
-    "ProcessTriTessFactorsAvg",
-    "ProcessTriTessFactorsMax",
-    "ProcessTriTessFactorsMin",
-    "RWBuffer",
-    "RWByteAddressBuffer",
-    "RWStructuredBuffer",
-    "RWTexture1D",
-    "RWTexture1DArray",
-    "RWTexture2D",
-    "RWTexture2DArray",
-    "RWTexture3D",
-    "RasterizerState",
-    "RenderTargetView",
-    "SV_ClipDistance",
-    "SV_Coverage",
-    "SV_CullDistance",
-    "SV_Depth",
-    "SV_DepthGreaterEqual",
-    "SV_DepthLessEqual",
-    "SV_DispatchThreadID",
-    "SV_DomainLocation",
-    "SV_GSInstanceID",
-    "SV_GroupID",
-    "SV_GroupIndex",
-    "SV_GroupThreadID",
-    "SV_InnerCoverage",
-    "SV_InsideTessFactor",
-    "SV_InstanceID",
-    "SV_IsFrontFace",
-    "SV_OutputControlPointID",
-    "SV_Position",
-    "SV_PrimitiveID",
-    "SV_RenderTargetArrayIndex",
-    "SV_SampleIndex",
-    "SV_StencilRef",
-    "SV_Target",
-    "SV_TessFactor",
-    "SV_VertexArrayIndex",
-    "SV_VertexID",
-    "Sampler",
-    "Sampler1D",
-    "Sampler2D",
-    "Sampler3D",
-    "SamplerCUBE",
-    "SamplerComparisonState",
-    "SamplerState",
-    "StructuredBuffer",
-    "TANGENT",
-    "TESSFACTOR",
-    "TEXCOORD",
-    "Texcoord",
-    "Texture",
-    "Texture1D",
-    "Texture1DArray",
-    "Texture2D",
-    "Texture2DArray",
-    "Texture2DMS",
-    "Texture2DMSArray",
-    "Texture3D",
-    "TextureCube",
-    "TextureCubeArray",
-    "TriangleStream",
-    "VFACE",
-    "VPOS",
-    "VertexShader",
-    "abort",
-    "allow_uav_condition",
-    "asdouble",
-    "asfloat",
-    "asint",
-    "asm",
-    "asm_fragment",
-    "asuint",
-    "auto",
-    "bool",
-    "bool1",
-    "bool1x1",
-    "bool1x2",
-    "bool1x3",
-    "bool1x4",
-    "bool2",
-    "bool2x1",
-    "bool2x2",
-    "bool2x3",
-    "bool2x4",
-    "bool3",
-    "bool3x1",
-    "bool3x2",
-    "bool3x3",
-    "bool3x4",
-    "bool4",
-    "bool4x1",
-    "bool4x2",
-    "bool4x3",
-    "bool4x4",
-    "branch",
-    "break",
-    "call",
-    "case",
-    "catch",
-    "cbuffer",
-    "centroid",
-    "char",
-    "class",
-    "clip",
-    "column_major",
-    "compile",
-    "compile_fragment",
-    "const",
-    "const_cast",
-    "continue",
-    "countbits",
-    "ddx",
-    "ddx_coarse",
-    "ddx_fine",
-    "ddy",
-    "ddy_coarse",
-    "ddy_fine",
-    "default",
-    "degrees",
-    "delete",
-    "discard",
-    "do",
-    "double",
-    "double1",
-    "double1x1",
-    "double1x2",
-    "double1x3",
-    "double1x4",
-    "double2",
-    "double2x1",
-    "double2x2",
-    "double2x3",
-    "double2x4",
-    "double3",
-    "double3x1",
-    "double3x2",
-    "double3x3",
-    "double3x4",
-    "double4",
-    "double4x1",
-    "double4x2",
-    "double4x3",
-    "double4x4",
-    "dst",
-    "dword",
-    "dword1",
-    "dword1x1",
-    "dword1x2",
-    "dword1x3",
-    "dword1x4",
-    "dword2",
-    "dword2x1",
-    "dword2x2",
-    "dword2x3",
-    "dword2x4",
-    "dword3",
-    "dword3x1",
-    "dword3x2",
-    "dword3x3",
-    "dword3x4",
-    "dword4",
-    "dword4x1",
-    "dword4x2",
-    "dword4x3",
-    "dword4x4",
-    "dynamic_cast",
-    "else",
-    "enum",
-    "errorf",
-    "explicit",
-    "export",
-    "extern",
-    "f16to32",
-    "f32tof16",
-    "false",
-    "fastopt",
-    "firstbithigh",
-    "firstbitlow",
-    "flatten",
-    "float",
-    "float1",
-    "float1x1",
-    "float1x2",
-    "float1x3",
-    "float1x4",
-    "float2",
-    "float2x1",
-    "float2x2",
-    "float2x3",
-    "float2x4",
-    "float3",
-    "float3x1",
-    "float3x2",
-    "float3x3",
-    "float3x4",
-    "float4",
-    "float4x1",
-    "float4x2",
-    "float4x3",
-    "float4x4",
-    "fmod",
-    "for",
-    "forcecase",
-    "frac",
-    "friend",
-    "fxgroup",
-    "goto",
-    "groupshared",
-    "half",
-    "half1",
-    "half1x1",
-    "half1x2",
-    "half1x3",
-    "half1x4",
-    "half2",
-    "half2x1",
-    "half2x2",
-    "half2x3",
-    "half2x4",
-    "half3",
-    "half3x1",
-    "half3x2",
-    "half3x3",
-    "half3x4",
-    "half4",
-    "half4x1",
-    "half4x2",
-    "half4x3",
-    "half4x4",
-    "if",
-    "in",
-    "inline",
-    "inout",
-    "int",
-    "int1",
-    "int1x1",
-    "int1x2",
-    "int1x3",
-    "int1x4",
-    "int2",
-    "int2x1",
-    "int2x2",
-    "int2x3",
-    "int2x4",
-    "int3",
-    "int3x1",
-    "int3x2",
-    "int3x3",
-    "int3x4",
-    "int4",
-    "int4x1",
-    "int4x2",
-    "int4x3",
-    "int4x4",
-    "interface",
-    "isfinite",
-    "isinf",
-    "isnan",
-    "lerp",
-    "line",
-    "lineadj",
-    "linear",
-    "lit",
-    "log10",
-    "long",
-    "loop",
-    "mad",
-    "matrix",
-    "min10float",
-    "min10float1",
-    "min10float1x1",
-    "min10float1x2",
-    "min10float1x3",
-    "min10float1x4",
-    "min10float2",
-    "min10float2x1",
-    "min10float2x2",
-    "min10float2x3",
-    "min10float2x4",
-    "min10float3",
-    "min10float3x1",
-    "min10float3x2",
-    "min10float3x3",
-    "min10float3x4",
-    "min10float4",
-    "min10float4x1",
-    "min10float4x2",
-    "min10float4x3",
-    "min10float4x4",
-    "min12int",
-    "min12int1",
-    "min12int1x1",
-    "min12int1x2",
-    "min12int1x3",
-    "min12int1x4",
-    "min12int2",
-    "min12int2x1",
-    "min12int2x2",
-    "min12int2x3",
-    "min12int2x4",
-    "min12int3",
-    "min12int3x1",
-    "min12int3x2",
-    "min12int3x3",
-    "min12int3x4",
-    "min12int4",
-    "min12int4x1",
-    "min12int4x2",
-    "min12int4x3",
-    "min12int4x4",
-    "min16float",
-    "min16float1",
-    "min16float1x1",
-    "min16float1x2",
-    "min16float1x3",
-    "min16float1x4",
-    "min16float2",
-    "min16float2x1",
-    "min16float2x2",
-    "min16float2x3",
-    "min16float2x4",
-    "min16float3",
-    "min16float3x1",
-    "min16float3x2",
-    "min16float3x3",
-    "min16float3x4",
-    "min16float4",
-    "min16float4x1",
-    "min16float4x2",
-    "min16float4x3",
-    "min16float4x4",
-    "min16int",
-    "min16int1",
-    "min16int1x1",
-    "min16int1x2",
-    "min16int1x3",
-    "min16int1x4",
-    "min16int2",
-    "min16int2x1",
-    "min16int2x2",
-    "min16int2x3",
-    "min16int2x4",
-    "min16int3",
-    "min16int3x1",
-    "min16int3x2",
-    "min16int3x3",
-    "min16int3x4",
-    "min16int4",
-    "min16int4x1",
-    "min16int4x2",
-    "min16int4x3",
-    "min16int4x4",
-    "min16uint",
-    "min16uint1",
-    "min16uint1x1",
-    "min16uint1x2",
-    "min16uint1x3",
-    "min16uint1x4",
-    "min16uint2",
-    "min16uint2x1",
-    "min16uint2x2",
-    "min16uint2x3",
-    "min16uint2x4",
-    "min16uint3",
-    "min16uint3x1",
-    "min16uint3x2",
-    "min16uint3x3",
-    "min16uint3x4",
-    "min16uint4",
-    "min16uint4x1",
-    "min16uint4x2",
-    "min16uint4x3",
-    "min16uint4x4",
-    "msad4",
-    "mul",
-    "mutable",
-    "namespace",
-    "new",
-    "nointerpolation",
-    "noise",
-    "noperspective",
-    "numthreads",
-    "operator",
-    "out",
-    "packoffset",
-    "pass",
-    "pixelfragment",
-    "pixelshader",
-    "point",
-    "precise",
-    "printf",
-    "private",
-    "protected",
-    "public",
-    "radians",
-    "rcp",
-    "refract",
-    "register",
-    "reinterpret_cast",
-    "return",
-    "row_major",
-    "rsqrt",
-    "sample",
-    "sampler",
-    "sampler1D",
-    "sampler2D",
-    "sampler3D",
-    "samplerCUBE",
-    "sampler_state",
-    "saturate",
-    "shared",
-    "short",
-    "signed",
-    "sincos",
-    "sizeof",
-    "snorm",
-    "stateblock",
-    "stateblock_state",
-    "static",
-    "static_cast",
-    "string",
-    "struct",
-    "switch",
-    "tbuffer",
-    "technique",
-    "technique10",
-    "technique11",
-    "template",
-    "tex1D",
-    "tex1Dbias",
-    "tex1Dgrad",
-    "tex1Dlod",
-    "tex1Dproj",
-    "tex2D",
-    "tex2Dbias",
-    "tex2Dgrad",
-    "tex2Dlod",
-    "tex2Dproj",
-    "tex3D",
-    "tex3Dbias",
-    "tex3Dgrad",
-    "tex3Dlod",
-    "tex3Dproj",
-    "texCUBE",
-    "texCUBEbias",
-    "texCUBEgrad",
-    "texCUBElod",
-    "texCUBEproj",
-    "texture",
-    "texture1D",
-    "texture1DArray",
-    "texture2D",
-    "texture2DArray",
-    "texture2DMS",
-    "texture2DMSArray",
-    "texture3D",
-    "textureCube",
-    "textureCubeArray",
-    "this",
-    "throw",
-    "transpose",
-    "triangle",
-    "triangleadj",
-    "true",
-    "try",
-    "typedef",
-    "typename",
-    "uint",
-    "uint1",
-    "uint1x1",
-    "uint1x2",
-    "uint1x3",
-    "uint1x4",
-    "uint2",
-    "uint2x1",
-    "uint2x2",
-    "uint2x3",
-    "uint2x4",
-    "uint3",
-    "uint3x1",
-    "uint3x2",
-    "uint3x3",
-    "uint3x4",
-    "uint4",
-    "uint4x1",
-    "uint4x2",
-    "uint4x3",
-    "uint4x4",
-    "uniform",
-    "union",
-    "unorm",
-    "unroll",
-    "unsigned",
-    "using",
-    "vector",
-    "vertexfragment",
-    "vertexshader",
-    "virtual",
-    "void",
-    "volatile",
-    "while",
-};
-
-// This list is used for a binary search and must be kept in sorted order.
-static constexpr std::array<const char*, 270> kReservedKeywordsMSL = {
-    "HUGE_VALF",
-    "HUGE_VALH",
-    "INFINITY",
-    "MAXFLOAT",
-    "MAXHALF",
-    "M_1_PI_F",
-    "M_1_PI_H",
-    "M_2_PI_F",
-    "M_2_PI_H",
-    "M_2_SQRTPI_F",
-    "M_2_SQRTPI_H",
-    "M_E_F",
-    "M_E_H",
-    "M_LN10_F",
-    "M_LN10_H",
-    "M_LN2_F",
-    "M_LN2_H",
-    "M_LOG10E_F",
-    "M_LOG10E_H",
-    "M_LOG2E_F",
-    "M_LOG2E_H",
-    "M_PI_2_F",
-    "M_PI_2_H",
-    "M_PI_4_F",
-    "M_PI_4_H",
-    "M_PI_F",
-    "M_PI_H",
-    "M_SQRT1_2_F",
-    "M_SQRT1_2_H",
-    "M_SQRT2_F",
-    "M_SQRT2_H",
-    "NAN",
-    "access",
-    "alignas",
-    "alignof",
-    "and",
-    "and_eq",
-    "array",
-    "array_ref",
-    "as_type",
-    "asm",
-    "atomic",
-    "atomic_bool",
-    "atomic_int",
-    "atomic_uint",
-    "auto",
-    "bitand",
-    "bitor",
-    "bool",
-    "bool2",
-    "bool3",
-    "bool4",
-    "break",
-    "buffer",
-    "case",
-    "catch",
-    "char",
-    "char16_t",
-    "char2",
-    "char3",
-    "char32_t",
-    "char4",
-    "class",
-    "compl",
-    "const",
-    "const_cast",
-    "const_reference",
-    "constant",
-    "constexpr",
-    "continue",
-    "decltype",
-    "default",
-    "delete",
-    "depth2d",
-    "depth2d_array",
-    "depth2d_ms",
-    "depth2d_ms_array",
-    "depthcube",
-    "depthcube_array",
-    "device",
-    "discard_fragment",
-    "do",
-    "double",
-    "dynamic_cast",
-    "else",
-    "enum",
-    "explicit",
-    "extern",
-    "false",
-    "final",
-    "float",
-    "float2",
-    "float2x2",
-    "float2x3",
-    "float2x4",
-    "float3",
-    "float3x2",
-    "float3x3",
-    "float3x4",
-    "float4",
-    "float4x2",
-    "float4x3",
-    "float4x4",
-    "for",
-    "fragment",
-    "friend",
-    "goto",
-    "half",
-    "half2",
-    "half2x2",
-    "half2x3",
-    "half2x4",
-    "half3",
-    "half3x2",
-    "half3x3",
-    "half3x4",
-    "half4",
-    "half4x2",
-    "half4x3",
-    "half4x4",
-    "if",
-    "imageblock",
-    "infinity",
-    "inline",
-    "int",
-    "int16_t",
-    "int2",
-    "int3",
-    "int32_t",
-    "int4",
-    "int64_t",
-    "int8_t",
-    "kernel",
-    "long",
-    "long2",
-    "long3",
-    "long4",
-    "main",
-    "matrix",
-    "metal",
-    "mutable",
-    "namespace",
-    "new",
-    "noexcept",
-    "not",
-    "not_eq",
-    "nullptr",
-    "operator",
-    "or",
-    "or_eq",
-    "override",
-    "packed_bool2",
-    "packed_bool3",
-    "packed_bool4",
-    "packed_char2",
-    "packed_char3",
-    "packed_char4",
-    "packed_float2",
-    "packed_float3",
-    "packed_float4",
-    "packed_half2",
-    "packed_half3",
-    "packed_half4",
-    "packed_int2",
-    "packed_int3",
-    "packed_int4",
-    "packed_short2",
-    "packed_short3",
-    "packed_short4",
-    "packed_uchar2",
-    "packed_uchar3",
-    "packed_uchar4",
-    "packed_uint2",
-    "packed_uint3",
-    "packed_uint4",
-    "packed_ushort2",
-    "packed_ushort3",
-    "packed_ushort4",
-    "patch_control_point",
-    "private",
-    "protected",
-    "ptrdiff_t",
-    "public",
-    "r16snorm",
-    "r16unorm",
-    "r8unorm",
-    "reference",
-    "register",
-    "reinterpret_cast",
-    "return",
-    "rg11b10f",
-    "rg16snorm",
-    "rg16unorm",
-    "rg8snorm",
-    "rg8unorm",
-    "rgb10a2",
-    "rgb9e5",
-    "rgba16snorm",
-    "rgba16unorm",
-    "rgba8snorm",
-    "rgba8unorm",
-    "sampler",
-    "short",
-    "short2",
-    "short3",
-    "short4",
-    "signed",
-    "size_t",
-    "sizeof",
-    "srgba8unorm",
-    "static",
-    "static_assert",
-    "static_cast",
-    "struct",
-    "switch",
-    "template",
-    "texture",
-    "texture1d",
-    "texture1d_array",
-    "texture2d",
-    "texture2d_array",
-    "texture2d_ms",
-    "texture2d_ms_array",
-    "texture3d",
-    "texture_buffer",
-    "texturecube",
-    "texturecube_array",
-    "this",
-    "thread",
-    "thread_local",
-    "threadgroup",
-    "threadgroup_imageblock",
-    "throw",
-    "true",
-    "try",
-    "typedef",
-    "typeid",
-    "typename",
-    "uchar",
-    "uchar2",
-    "uchar3",
-    "uchar4",
-    "uint",
-    "uint16_t",
-    "uint2",
-    "uint3",
-    "uint32_t",
-    "uint4",
-    "uint64_t",
-    "uint8_t",
-    "ulong2",
-    "ulong3",
-    "ulong4",
-    "uniform",
-    "union",
-    "unsigned",
-    "ushort",
-    "ushort2",
-    "ushort3",
-    "ushort4",
-    "using",
-    "vec",
-    "vertex",
-    "virtual",
-    "void",
-    "volatile",
-    "wchar_t",
-    "while",
-    "xor",
-    "xor_eq",
-};
-
-}  // namespace
-
-Renamer::Config::Config() = default;
-
-Renamer::Config::Config(Target t) : target(t) {}
-
-Renamer::Config::Config(Target t, Remappings&& remappings)
-    : target(t), requested_names(std::move(remappings)) {}
-
-Renamer::Config::Config(const Config&) = default;
-
-Renamer::Config::~Config() = default;
-
-Renamer::Renamer() = default;
-Renamer::~Renamer() = default;
-
-Transform::ApplyResult Renamer::Apply(const Program& src, const DataMap& inputs, DataMap&) const {
-    Hashset<Symbol, 16> global_decls;
-    for (auto* decl : src.AST().TypeDecls()) {
-        global_decls.Add(decl->name->symbol);
-    }
-
-    // Identifiers that need to keep their symbols preserved.
-    Hashset<const Identifier*, 16> preserved_identifiers;
-
-    for (auto* node : src.ASTNodes().Objects()) {
-        auto preserve_if_builtin_type = [&](const Identifier* ident) {
-            if (!global_decls.Contains(ident->symbol)) {
-                preserved_identifiers.Add(ident);
-            }
-        };
-
-        Switch(
-            node,
-            [&](const MemberAccessorExpression* accessor) {
-                auto* sem = src.Sem().Get(accessor)->Unwrap();
-                if (sem->Is<sem::Swizzle>()) {
-                    preserved_identifiers.Add(accessor->member);
-                } else if (auto* str_expr = src.Sem().GetVal(accessor->object)) {
-                    if (auto* ty = str_expr->Type()->UnwrapPtrOrRef()->As<core::type::Struct>()) {
-                        if (!ty->Is<sem::Struct>()) {  // Builtin structure
-                            preserved_identifiers.Add(accessor->member);
-                        }
-                    }
-                }
-            },
-            [&](const DiagnosticAttribute* diagnostic) {
-                if (auto* category = diagnostic->control.rule_name->category) {
-                    preserved_identifiers.Add(category);
-                }
-                preserved_identifiers.Add(diagnostic->control.rule_name->name);
-            },
-            [&](const DiagnosticDirective* diagnostic) {
-                if (auto* category = diagnostic->control.rule_name->category) {
-                    preserved_identifiers.Add(category);
-                }
-                preserved_identifiers.Add(diagnostic->control.rule_name->name);
-            },
-            [&](const IdentifierExpression* expr) {
-                Switch(
-                    src.Sem().Get(expr),  //
-                    [&](const sem::BuiltinEnumExpressionBase*) {
-                        preserved_identifiers.Add(expr->identifier);
-                    },
-                    [&](const sem::TypeExpression*) {
-                        preserve_if_builtin_type(expr->identifier);
-                    });
-            },
-            [&](const CallExpression* call) {
-                Switch(
-                    src.Sem().Get(call)->UnwrapMaterialize()->As<sem::Call>()->Target(),
-                    [&](const sem::BuiltinFn*) {
-                        preserved_identifiers.Add(call->target->identifier);
-                    },
-                    [&](const sem::ValueConversion*) {
-                        preserve_if_builtin_type(call->target->identifier);
-                    },
-                    [&](const sem::ValueConstructor*) {
-                        preserve_if_builtin_type(call->target->identifier);
-                    });
-            });
-    }
-
-    Target target = Target::kAll;
-    const Remappings* requested_names = nullptr;
-
-    if (auto* cfg = inputs.Get<Config>()) {
-        target = cfg->target;
-        requested_names = &(cfg->requested_names);
-    }
-
-    // Returns true if the symbol should be renamed based on the input configuration settings.
-    auto should_rename = [&](Symbol symbol) {
-        if (target == Target::kAll) {
-            return true;
-        }
-        if (requested_names->count(symbol.Name())) {
-            return true;
-        }
-        auto name = symbol.Name();
-        if (!tint::utf8::IsASCII(name)) {
-            // name is non-ascii. All of the backend keywords are ascii, so rename.
-            return true;
-        }
-        switch (target) {
-            case Target::kGlslKeywords:
-                return std::binary_search(kReservedKeywordsGLSL.cbegin(),
-                                          kReservedKeywordsGLSL.cend(), name) ||
-                       name.compare(0, 3, "gl_") == 0 || name.find("__") != std::string::npos;
-            case Target::kHlslKeywords:
-                return std::binary_search(kReservedKeywordsHLSL.cbegin(),
-                                          kReservedKeywordsHLSL.cend(), name);
-            case Target::kMslKeywords:
-                return std::binary_search(kReservedKeywordsMSL.cbegin(),
-                                          kReservedKeywordsMSL.cend(), name);
-            default:
-                break;
-        }
-        return true;
-    };
-
-    Hashmap<Symbol, Symbol, 32> remappings;
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ false};
-
-    ctx.ReplaceAll([&](const Identifier* ident) -> const Identifier* {
-        const auto symbol = ident->symbol;
-        if (preserved_identifiers.Contains(ident) || !should_rename(symbol)) {
-            return nullptr;  // Preserve symbol
-        }
-
-        // Create a replacement for this symbol, if we haven't already.
-        auto replacement = remappings.GetOrAdd(symbol, [&] {
-            if (requested_names) {
-                auto iter = requested_names->find(symbol.Name());
-                if (iter != requested_names->end()) {
-                    // Use the explicitly given name for renaming this symbol
-                    // if the extra is given in the config.
-                    return b.Symbols().New(iter->second);
-                }
-            }
-            return b.Symbols().New();
-        });
-
-        // Reconstruct the identifier
-        if (auto* tmpl_ident = ident->As<TemplatedIdentifier>()) {
-            auto args = ctx.Clone(tmpl_ident->arguments);
-            return ctx.dst->create<TemplatedIdentifier>(ctx.Clone(ident->source), replacement,
-                                                        std::move(args), tint::Empty);
-        }
-        return ctx.dst->create<Identifier>(ctx.Clone(ident->source), replacement);
-    });
-
-    ctx.Clone();
-
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/renamer.h b/src/tint/lang/wgsl/ast/transform/renamer.h
deleted file mode 100644
index 8c1ff26..0000000
--- a/src/tint/lang/wgsl/ast/transform/renamer.h
+++ /dev/null
@@ -1,110 +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_WGSL_AST_TRANSFORM_RENAMER_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_RENAMER_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// Renamer is a Transform that renames all the symbols in a program.
-class Renamer final : public Castable<Renamer, Transform> {
-  public:
-    /// Remappings is a map of old symbol name to new symbol name
-    using Remappings = std::unordered_map<std::string, std::string>;
-
-    /// Target is an enumerator of rename targets that can be used
-    enum class Target {
-        /// Rename every symbol.
-        kAll,
-        /// Only rename symbols that are reserved keywords in GLSL.
-        kGlslKeywords,
-        /// Only rename symbols that are reserved keywords in HLSL.
-        kHlslKeywords,
-        /// Only rename symbols that are reserved keywords in MSL.
-        kMslKeywords,
-    };
-
-    /// Optional configuration options for the transform.
-    /// If omitted, then the renamer will use Target::kAll.
-    struct Config final : public Castable<Config, transform::Data> {
-        /// Constructor
-        Config();
-
-        /// Constructor
-        /// @param tgt the targets to rename
-        /// renamed
-        explicit Config(Target tgt);
-
-        /// Constructor
-        /// @param tgt the targets to rename
-        /// @param remappings requested old to new name map
-        Config(Target tgt, Remappings&& remappings);
-
-        /// Copy constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// The targets to rename
-        Target target = Target::kAll;
-
-        /// Requested renaming rules
-        Remappings requested_names = {};
-
-        /// Reflection for this class
-        TINT_REFLECT(Config, target, requested_names);
-    };
-
-    /// Constructor using a the configuration provided in the input Data
-    Renamer();
-
-    /// Destructor
-    ~Renamer() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-namespace tint {
-
-/// Reflection for Target
-TINT_REFLECT_ENUM_RANGE(tint::ast::transform::Renamer::Target, kAll, kMslKeywords);
-
-}  // namespace tint
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_RENAMER_H_
diff --git a/src/tint/lang/wgsl/ast/transform/renamer_test.cc b/src/tint/lang/wgsl/ast/transform/renamer_test.cc
deleted file mode 100644
index f7e9a7e..0000000
--- a/src/tint/lang/wgsl/ast/transform/renamer_test.cc
+++ /dev/null
@@ -1,2145 +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/wgsl/ast/transform/renamer.h"
-
-#include <memory>
-#include <unordered_set>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/core/builtin_type.h"
-#include "src/tint/lang/core/texel_format.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/utils/text/string.h"
-
-namespace tint::ast::transform {
-namespace {
-
-constexpr const char kUnicodeIdentifier[] =  // "𝖎𝖉𝖊𝖓𝖙𝖎𝖋𝖎𝖊𝖗123"
-    "\xf0\x9d\x96\x8e\xf0\x9d\x96\x89\xf0\x9d\x96\x8a\xf0\x9d\x96\x93"
-    "\xf0\x9d\x96\x99\xf0\x9d\x96\x8e\xf0\x9d\x96\x8b\xf0\x9d\x96\x8e"
-    "\xf0\x9d\x96\x8a\xf0\x9d\x96\x97\x31\x32\x33";
-
-using ::testing::ContainerEq;
-
-using RenamerTest = TransformTest;
-
-TEST_F(RenamerTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = "";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, BasicModuleVertexIndex) {
-    auto* src = R"(
-fn test(vert_idx : u32) -> u32 {
-  return vert_idx;
-}
-
-@vertex
-fn entry(@builtin(vertex_index) vert_idx : u32
-        ) -> @builtin(position) vec4<f32>  {
-  _ = test(vert_idx);
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-fn tint_symbol(tint_symbol_1 : u32) -> u32 {
-  return tint_symbol_1;
-}
-
-@vertex
-fn tint_symbol_2(@builtin(vertex_index) tint_symbol_1 : u32) -> @builtin(position) vec4<f32> {
-  _ = tint_symbol(tint_symbol_1);
-  return vec4<f32>();
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, RequestedNamesWithRenameAll) {
-    auto* src = R"(
-struct ShaderIO {
-    @location(1) var1: f32,
-    @location(3) @interpolate(flat) var3: u32,
-    @builtin(position) pos: vec4f,
-}
-
-@vertex fn main(@builtin(vertex_index) vert_idx : u32)
-     -> ShaderIO {
-  var pos = array(
-      vec2f(-1.0, 3.0),
-      vec2f(-1.0, -3.0),
-      vec2f(3.0, 0.0));
-
-  var shaderIO: ShaderIO;
-  shaderIO.var1 = 0.0;
-  shaderIO.var3 = 1u;
-  shaderIO.pos = vec4f(pos[vert_idx], 0.0, 1.0);
-
-  return shaderIO;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @location(1)
-  user_var1 : f32,
-  @location(3) @interpolate(flat)
-  user_var3 : u32,
-  @builtin(position)
-  tint_symbol_1 : vec4f,
-}
-
-@vertex
-fn tint_symbol_2(@builtin(vertex_index) tint_symbol_3 : u32) -> tint_symbol {
-  var tint_symbol_1 = array(vec2f(-(1.0), 3.0), vec2f(-(1.0), -(3.0)), vec2f(3.0, 0.0));
-  var tint_symbol_4 : tint_symbol;
-  tint_symbol_4.user_var1 = 0.0;
-  tint_symbol_4.user_var3 = 1u;
-  tint_symbol_4.tint_symbol_1 = vec4f(tint_symbol_1[tint_symbol_3], 0.0, 1.0);
-  return tint_symbol_4;
-}
-)";
-
-    DataMap inputs;
-    inputs.Add<Renamer::Config>(Renamer::Target::kAll,
-                                /* remappings */
-                                Renamer::Remappings{
-                                    {"var1", "user_var1"},
-                                    {"var3", "user_var3"},
-                                });
-    auto got = Run<Renamer>(src, inputs);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-using RenamerTestRequestedNamesWithoutRenameAll = TransformTestWithParam<Renamer::Target>;
-
-TEST_P(RenamerTestRequestedNamesWithoutRenameAll, RequestedNames) {
-    auto* src = R"(
-struct ShaderIO {
-    @location(1) var1: f32,
-    @location(3) @interpolate(flat) var3: u32,
-    @builtin(position) pos: vec4f,
-}
-
-@vertex fn entry_point(@builtin(vertex_index) vert_idx : u32)
-     -> ShaderIO {
-  var pos = array(
-      vec2f(-1.0, 3.0),
-      vec2f(-1.0, -3.0),
-      vec2f(3.0, 0.0));
-
-  var shaderIO: ShaderIO;
-  shaderIO.var1 = 0.0;
-  shaderIO.var3 = 1u;
-  shaderIO.pos = vec4f(pos[vert_idx], 0.0, 1.0);
-
-  return shaderIO;
-}
-)";
-
-    auto* expect = R"(
-struct ShaderIO {
-  @location(1)
-  user_var1 : f32,
-  @location(3) @interpolate(flat)
-  user_var3 : u32,
-  @builtin(position)
-  pos : vec4f,
-}
-
-@vertex
-fn entry_point(@builtin(vertex_index) vert_idx : u32) -> ShaderIO {
-  var pos = array(vec2f(-(1.0), 3.0), vec2f(-(1.0), -(3.0)), vec2f(3.0, 0.0));
-  var shaderIO : ShaderIO;
-  shaderIO.user_var1 = 0.0;
-  shaderIO.user_var3 = 1u;
-  shaderIO.pos = vec4f(pos[vert_idx], 0.0, 1.0);
-  return shaderIO;
-}
-)";
-
-    DataMap inputs;
-    inputs.Add<Renamer::Config>(GetParam(),
-                                /* remappings */
-                                Renamer::Remappings{
-                                    {"var1", "user_var1"},
-                                    {"var3", "user_var3"},
-                                });
-    auto got = Run<Renamer>(src, inputs);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(RenamerTestRequestedNamesWithoutRenameAll,
-                         RenamerTestRequestedNamesWithoutRenameAll,
-                         testing::Values(Renamer::Target::kGlslKeywords,
-                                         Renamer::Target::kHlslKeywords,
-                                         Renamer::Target::kMslKeywords));
-
-TEST_F(RenamerTest, PreserveSwizzles) {
-    auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
-  var v : vec4<f32>;
-  var rgba : f32;
-  var xyzw : f32;
-  var z : f32;
-  return v.zyxw + v.rgab * v.z;
-}
-)";
-
-    auto* expect = R"(
-@vertex
-fn tint_symbol() -> @builtin(position) vec4<f32> {
-  var tint_symbol_1 : vec4<f32>;
-  var tint_symbol_2 : f32;
-  var tint_symbol_3 : f32;
-  var tint_symbol_4 : f32;
-  return (tint_symbol_1.zyxw + (tint_symbol_1.rgab * tint_symbol_1.z));
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveSwizzles_ThroughMaterialize) {
-    auto* src = R"(
-const v = vec4();
-const x = v.x * 2u;
-)";
-
-    auto* expect = R"(
-const tint_symbol = vec4();
-
-const tint_symbol_1 = (tint_symbol.x * 2u);
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveBuiltins) {
-    auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
-  var blah : vec4<f32>;
-  return abs(blah);
-}
-)";
-
-    auto* expect = R"(
-@vertex
-fn tint_symbol() -> @builtin(position) vec4<f32> {
-  var tint_symbol_1 : vec4<f32>;
-  return abs(tint_symbol_1);
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveBuiltinTypes) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn entry() {
-  var a = modf(1.0).whole;
-  var b = modf(1.0).fract;
-  var c = frexp(1.0).fract;
-  var d = frexp(1.0).exp;
-}
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn tint_symbol() {
-  var tint_symbol_1 = modf(1.0).whole;
-  var tint_symbol_2 = modf(1.0).fract;
-  var tint_symbol_3 = frexp(1.0).fract;
-  var tint_symbol_4 = frexp(1.0).exp;
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveBuiltinTypes_ViaPointerDot) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn entry() {
-  var m = modf(1.0);
-  let p1 = &m;
-  var f = frexp(1.0);
-  let p2 = &f;
-
-  var a = p1.whole;
-  var b = p1.fract;
-  var c = p2.fract;
-  var d = p2.exp;
-}
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn tint_symbol() {
-  var tint_symbol_1 = modf(1.0);
-  let tint_symbol_2 = &(tint_symbol_1);
-  var tint_symbol_3 = frexp(1.0);
-  let tint_symbol_4 = &(tint_symbol_3);
-  var tint_symbol_5 = tint_symbol_2.whole;
-  var tint_symbol_6 = tint_symbol_2.fract;
-  var tint_symbol_7 = tint_symbol_4.fract;
-  var tint_symbol_8 = tint_symbol_4.exp;
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveCoreDiagnosticRuleName) {
-    auto* src = R"(
-diagnostic(off, chromium.unreachable_code);
-
-@diagnostic(off, derivative_uniformity)
-@fragment
-fn entry(@location(0) value : f32) -> @location(0) f32 {
-  if (value > 0) {
-    return dpdx(value);
-    return 0.0;
-  }
-  return 1.0;
-}
-)";
-
-    auto* expect = R"(
-diagnostic(off, chromium.unreachable_code);
-
-@diagnostic(off, derivative_uniformity) @fragment
-fn tint_symbol(@location(0) tint_symbol_1 : f32) -> @location(0) f32 {
-  if ((tint_symbol_1 > 0)) {
-    return dpdx(tint_symbol_1);
-    return 0.0;
-  }
-  return 1.0;
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, AttemptSymbolCollision) {
-    auto* src = R"(
-@vertex
-fn entry() -> @builtin(position) vec4<f32> {
-  var tint_symbol : vec4<f32>;
-  var tint_symbol_2 : vec4<f32>;
-  var tint_symbol_4 : vec4<f32>;
-  return tint_symbol + tint_symbol_2 + tint_symbol_4;
-}
-)";
-
-    auto* expect = R"(
-@vertex
-fn tint_symbol() -> @builtin(position) vec4<f32> {
-  var tint_symbol_1 : vec4<f32>;
-  var tint_symbol_2 : vec4<f32>;
-  var tint_symbol_3 : vec4<f32>;
-  return ((tint_symbol_1 + tint_symbol_2) + tint_symbol_3);
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveTexelFormatAndAccess) {
-    auto src = R"(
-@group(0) @binding(0) var texture : texture_storage_2d<rgba8unorm, write>;
-
-fn f() {
-  var dims = textureDimensions(texture);
-}
-)";
-
-    auto expect = R"(
-@group(0) @binding(0) var tint_symbol : texture_storage_2d<rgba8unorm, write>;
-
-fn tint_symbol_1() {
-  var tint_symbol_2 = textureDimensions(tint_symbol);
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerTest, PreserveAddressSpace) {
-    auto src = R"(
-var<private> p : i32;
-
-fn f() {
-  var v = p;
-}
-)";
-
-    auto expect = R"(
-var<private> tint_symbol : i32;
-
-fn tint_symbol_1() {
-  var tint_symbol_2 = tint_symbol;
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-using RenamerTestGlsl = TransformTestWithParam<std::string>;
-using RenamerTestHlsl = TransformTestWithParam<std::string>;
-using RenamerTestMsl = TransformTestWithParam<std::string>;
-
-TEST_P(RenamerTestGlsl, Keywords) {
-    auto keyword = GetParam();
-
-    auto src = R"(
-@fragment
-fn frag_main() {
-  var )" + keyword +
-               R"( : i32;
-}
-)";
-
-    auto* expect = R"(
-@fragment
-fn frag_main() {
-  var tint_symbol : i32;
-}
-)";
-
-    DataMap inputs;
-    inputs.Add<Renamer::Config>(Renamer::Target::kGlslKeywords);
-    auto got = Run<Renamer>(src, inputs);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerTestHlsl, Keywords) {
-    auto keyword = GetParam();
-
-    auto src = R"(
-@fragment
-fn frag_main() {
-  var )" + keyword +
-               R"( : i32;
-}
-)";
-
-    auto* expect = R"(
-@fragment
-fn frag_main() {
-  var tint_symbol : i32;
-}
-)";
-
-    DataMap inputs;
-    inputs.Add<Renamer::Config>(Renamer::Target::kHlslKeywords);
-    auto got = Run<Renamer>(src, inputs);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerTestMsl, Keywords) {
-    auto keyword = GetParam();
-
-    auto src = R"(
-@fragment
-fn frag_main() {
-  var )" + keyword +
-               R"( : i32;
-}
-)";
-
-    auto* expect = R"(
-@fragment
-fn frag_main() {
-  var tint_symbol : i32;
-}
-)";
-
-    DataMap inputs;
-    inputs.Add<Renamer::Config>(Renamer::Target::kMslKeywords);
-    auto got = Run<Renamer>(src, inputs);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(RenamerTestGlsl,
-                         RenamerTestGlsl,
-                         testing::Values(  // "active",   // Also reserved in WGSL
-                                           // "asm",       // WGSL keyword
-                             "atomic_uint",
-                             // "attribute",  // Also reserved in WGSL
-                             // "bool",      // WGSL keyword
-                             // "break",     // WGSL keyword
-                             "buffer",
-                             "bvec2",
-                             "bvec3",
-                             "bvec4",
-                             //    "case",      // WGSL keyword
-                             // "cast",  // Also reserved in WGSL
-                             "centroid",
-                             // "class",  // Also reserved in WGSL
-                             // "coherent",  // Also reserved in WGSL
-                             // "common",  // Also reserved in WGSL
-                             // "const",     // WGSL keyword
-                             // "continue",  // WGSL keyword
-                             // "default",   // WGSL keyword
-                             // "discard",   // WGSL keyword
-                             "dmat2",
-                             "dmat2x2",
-                             "dmat2x3",
-                             "dmat2x4",
-                             "dmat3",
-                             "dmat3x2",
-                             "dmat3x3",
-                             "dmat3x4",
-                             "dmat4",
-                             "dmat4x2",
-                             "dmat4x3",
-                             "dmat4x4",
-                             // "do",         // WGSL keyword
-                             "double",
-                             "dvec2",
-                             "dvec3",
-                             "dvec4",
-                             // "else"        // WGSL keyword
-                             // "enum",       // WGSL keyword
-                             // "extern",  // Also reserved in WGSL
-                             // "external",  // Also reserved in WGSL
-                             // "false",      // WGSL keyword
-                             // "filter",  // Also reserved in WGSL
-                             "fixed",
-                             "flat",
-                             "float",
-                             // "for",        // WGSL keyword
-                             "fvec2",
-                             "fvec3",
-                             "fvec4",
-                             "gl_BaseInstance",
-                             "gl_BaseVertex",
-                             "gl_ClipDistance",
-                             "gl_DepthRangeParameters",
-                             "gl_DrawID",
-                             "gl_FragCoord",
-                             "gl_FragDepth",
-                             "gl_FrontFacing",
-                             "gl_GlobalInvocationID",
-                             "gl_InstanceID",
-                             "gl_LocalInvocationID",
-                             "gl_LocalInvocationIndex",
-                             "gl_NumSamples",
-                             "gl_NumWorkGroups",
-                             "gl_PerVertex",
-                             "gl_PointCoord",
-                             "gl_PointSize",
-                             "gl_Position",
-                             "gl_PrimitiveID",
-                             "gl_SampleID",
-                             "gl_SampleMask",
-                             "gl_SampleMaskIn",
-                             "gl_SamplePosition",
-                             "gl_VertexID",
-                             "gl_WorkGroupID",
-                             "gl_WorkGroupSize",
-                             // "goto",  // Also reserved in WGSL
-                             "half",
-                             // "highp",  // Also reserved in WGSL
-                             "hvec2",
-                             "hvec3",
-                             "hvec4",
-                             // "if",         // WGSL keyword
-                             "iimage1D",
-                             "iimage1DArray",
-                             "iimage2D",
-                             "iimage2DArray",
-                             "iimage2DMS",
-                             "iimage2DMSArray",
-                             "iimage2DRect",
-                             "iimage3D",
-                             "iimageBuffer",
-                             "iimageCube",
-                             "iimageCubeArray",
-                             "image1D",
-                             "image1DArray",
-                             "image2D",
-                             "image2DArray",
-                             "image2DMS",
-                             "image2DMSArray",
-                             "image2DRect",
-                             "image3D",
-                             "imageBuffer",
-                             "imageCube",
-                             "imageCubeArray",
-                             "in",
-                             // "inline",  // Also reserved in WGSL
-                             // "inout",  // Also reserved in WGSL
-                             "input",
-                             "int",
-                             // "interface",  // Also reserved in WGSL
-                             // "invariant",  // Also reserved in WGSL
-                             "isampler1D",
-                             "isampler1DArray",
-                             "isampler2D",
-                             "isampler2DArray",
-                             "isampler2DMS",
-                             "isampler2DMSArray",
-                             "isampler2DRect",
-                             "isampler3D",
-                             "isamplerBuffer",
-                             "isamplerCube",
-                             "isamplerCubeArray",
-                             "ivec2",
-                             "ivec3",
-                             "ivec4",
-                             // "layout",  // Also reserved in WGSL
-                             "long",
-                             // "lowp",  // Also reserved in WGSL
-                             // "mat2x2",      // WGSL keyword
-                             // "mat2x3",      // WGSL keyword
-                             // "mat2x4",      // WGSL keyword
-                             // "mat2",
-                             "mat3",
-                             // "mat3x2",      // WGSL keyword
-                             // "mat3x3",      // WGSL keyword
-                             // "mat3x4",      // WGSL keyword
-                             "mat4",
-                             // "mat4x2",      // WGSL keyword
-                             // "mat4x3",      // WGSL keyword
-                             // "mat4x4",      // WGSL keyword
-                             // "mediump",  // Also reserved in WGSL
-                             // "namespace",  // Also reserved in WGSL
-                             // "noinline",  // Also reserved in WGSL
-                             // "noncoherent",  // Reserved in WGSL
-                             // "noperspective",  // Also reserved in WGSL
-                             // "non_coherent",  // Reserved in WGSL
-                             "out",
-                             "output",
-                             // "partition",  // Also reserved in WGSL
-                             // "patch",  // Also reserved in WGSL
-                             // "precise",  // Also reserved in WGSL
-                             // "precision",  // Also reserved in WGSL
-                             // "public",  // Also reserved in WGSL
-                             // "readonly",  // Also reserved in WGSL
-                             // "resource",  // Also reserved in WGSL
-                             // "restrict",  // Also reserved in WGSL
-                             // "return",     // WGSL keyword
-                             "sample",
-                             "sampler1D",
-                             "sampler1DArray",
-                             "sampler1DArrayShadow",
-                             "sampler1DShadow",
-                             "sampler2D",
-                             "sampler2DArray",
-                             "sampler2DArrayShadow",
-                             "sampler2DMS",
-                             "sampler2DMSArray",
-                             "sampler2DRect",
-                             "sampler2DRectShadow",
-                             "sampler2DShadow",
-                             "sampler3D",
-                             "sampler3DRect",
-                             "samplerBuffer",
-                             "samplerCube",
-                             "samplerCubeArray",
-                             "samplerCubeArrayShadow",
-                             "samplerCubeShadow",
-                             // "shared"  // Also reserved in WGSL,
-                             "short",
-                             // "sizeof",  // Also reserved in WGSL
-                             // "smooth",  // Also reserved in WGSL
-                             // "static",  // Also reserved in WGSL
-                             // "struct",     // WGSL keyword
-                             // "subroutine",  // Also reserved in WGSL
-                             "superp",
-                             // "switch",     // WGSL keyword
-                             // "template",  // Also reserved in WGSL
-                             // "this",  // Also reserved in WGSL
-                             // "true",       // WGSL keyword
-                             // "typedef",    // WGSL keyword
-                             "uimage1D",
-                             "uimage1DArray",
-                             "uimage2D",
-                             "uimage2DArray",
-                             "uimage2DMS",
-                             "uimage2DMSArray",
-                             "uimage2DRect",
-                             "uimage3D",
-                             "uimageBuffer",
-                             "uimageCube",
-                             "uimageCubeArray",
-                             "uint",
-                             // "uniform",    // WGSL keyword
-                             // "union",  // Also reserved in WGSL
-                             "unsigned",
-                             "usampler1D",
-                             "usampler1DArray",
-                             "usampler2D",
-                             "usampler2DArray",
-                             "usampler2DMS",
-                             "usampler2DMSArray",
-                             "usampler2DRect",
-                             "usampler3D",
-                             "usamplerBuffer",
-                             "usamplerCube",
-                             "usamplerCubeArray",
-                             // "using",      // WGSL keyword
-                             "uvec2",
-                             "uvec3",
-                             "uvec4",
-                             // "varying",  // Also reserved in WGSL
-                             // "vec2",       // WGSL keyword
-                             // "vec3",       // WGSL keyword
-                             // "vec4",       // WGSL keyword
-                             // "void",       // WGSL keyword
-                             // "volatile",  // Also reserved in WGSL
-                             // "while",      // WGSL keyword
-                             // "writeonly",  // Also reserved in WGSL
-                             kUnicodeIdentifier));
-
-INSTANTIATE_TEST_SUITE_P(RenamerTestHlsl,
-                         RenamerTestHlsl,
-                         testing::Values("AddressU",
-                                         "AddressV",
-                                         "AddressW",
-                                         "AllMemoryBarrier",
-                                         "AllMemoryBarrierWithGroupSync",
-                                         "AppendStructuredBuffer",
-                                         "BINORMAL",
-                                         "BLENDINDICES",
-                                         "BLENDWEIGHT",
-                                         "BlendState",
-                                         "BorderColor",
-                                         "Buffer",
-                                         "ByteAddressBuffer",
-                                         "COLOR",
-                                         "CheckAccessFullyMapped",
-                                         "ComparisonFunc",
-                                         // "CompileShader",  // Also reserved in WGSL
-                                         // "ComputeShader",  // Also reserved in WGSL
-                                         "ConsumeStructuredBuffer",
-                                         "D3DCOLORtoUBYTE4",
-                                         "DEPTH",
-                                         "DepthStencilState",
-                                         "DepthStencilView",
-                                         "DeviceMemoryBarrier",
-                                         "DeviceMemroyBarrierWithGroupSync",
-                                         // "DomainShader",  // Also reserved in WGSL
-                                         "EvaluateAttributeAtCentroid",
-                                         "EvaluateAttributeAtSample",
-                                         "EvaluateAttributeSnapped",
-                                         "FOG",
-                                         "Filter",
-                                         // "GeometryShader",  // Also reserved in WGSL
-                                         "GetRenderTargetSampleCount",
-                                         "GetRenderTargetSamplePosition",
-                                         "GroupMemoryBarrier",
-                                         "GroupMemroyBarrierWithGroupSync",
-                                         // "Hullshader",  // Also reserved in WGSL
-                                         "InputPatch",
-                                         "InterlockedAdd",
-                                         "InterlockedAnd",
-                                         "InterlockedCompareExchange",
-                                         "InterlockedCompareStore",
-                                         "InterlockedExchange",
-                                         "InterlockedMax",
-                                         "InterlockedMin",
-                                         "InterlockedOr",
-                                         "InterlockedXor",
-                                         "LineStream",
-                                         "MaxAnisotropy",
-                                         "MaxLOD",
-                                         "MinLOD",
-                                         "MipLODBias",
-                                         "NORMAL",
-                                         // "NULL",  // Also reserved in WGSL
-                                         "Normal",
-                                         "OutputPatch",
-                                         "POSITION",
-                                         "POSITIONT",
-                                         "PSIZE",
-                                         "PixelShader",
-                                         "PointStream",
-                                         "Process2DQuadTessFactorsAvg",
-                                         "Process2DQuadTessFactorsMax",
-                                         "Process2DQuadTessFactorsMin",
-                                         "ProcessIsolineTessFactors",
-                                         "ProcessQuadTessFactorsAvg",
-                                         "ProcessQuadTessFactorsMax",
-                                         "ProcessQuadTessFactorsMin",
-                                         "ProcessTriTessFactorsAvg",
-                                         "ProcessTriTessFactorsMax",
-                                         "ProcessTriTessFactorsMin",
-                                         "RWBuffer",
-                                         "RWByteAddressBuffer",
-                                         "RWStructuredBuffer",
-                                         "RWTexture1D",
-                                         "RWTexture1DArray",
-                                         "RWTexture2D",
-                                         "RWTexture2DArray",
-                                         "RWTexture3D",
-                                         "RasterizerState",
-                                         "RenderTargetView",
-                                         "SV_ClipDistance",
-                                         "SV_Coverage",
-                                         "SV_CullDistance",
-                                         "SV_Depth",
-                                         "SV_DepthGreaterEqual",
-                                         "SV_DepthLessEqual",
-                                         "SV_DispatchThreadID",
-                                         "SV_DomainLocation",
-                                         "SV_GSInstanceID",
-                                         "SV_GroupID",
-                                         "SV_GroupIndex",
-                                         "SV_GroupThreadID",
-                                         "SV_InnerCoverage",
-                                         "SV_InsideTessFactor",
-                                         "SV_InstanceID",
-                                         "SV_IsFrontFace",
-                                         "SV_OutputControlPointID",
-                                         "SV_Position",
-                                         "SV_PrimitiveID",
-                                         "SV_RenderTargetArrayIndex",
-                                         "SV_SampleIndex",
-                                         "SV_StencilRef",
-                                         "SV_Target",
-                                         "SV_TessFactor",
-                                         "SV_VertexArrayIndex",
-                                         "SV_VertexID",
-                                         "Sampler",
-                                         "Sampler1D",
-                                         "Sampler2D",
-                                         "Sampler3D",
-                                         "SamplerCUBE",
-                                         "SamplerComparisonState",
-                                         "SamplerState",
-                                         "StructuredBuffer",
-                                         "TANGENT",
-                                         "TESSFACTOR",
-                                         "TEXCOORD",
-                                         "Texcoord",
-                                         "Texture",
-                                         "Texture1D",
-                                         "Texture1DArray",
-                                         "Texture2D",
-                                         "Texture2DArray",
-                                         "Texture2DMS",
-                                         "Texture2DMSArray",
-                                         "Texture3D",
-                                         "TextureCube",
-                                         "TextureCubeArray",
-                                         "TriangleStream",
-                                         "VFACE",
-                                         "VPOS",
-                                         "VertexShader",
-                                         "abort",
-                                         // "abs",  // WGSL builtin
-                                         // "acos",  // WGSL builtin
-                                         // "all",  // WGSL builtin
-                                         "allow_uav_condition",
-                                         // "any",  // WGSL builtin
-                                         "asdouble",
-                                         "asfloat",
-                                         // "asin",  // WGSL builtin
-                                         "asint",
-                                         // "asm",  // WGSL keyword
-                                         // "asm_fragment",  // Also reserved in WGSL
-                                         "asuint",
-                                         // "atan",  // WGSL builtin
-                                         // "atan2",  // WGSL builtin
-                                         // "auto",  // Also reserved in WGSL
-                                         // "bool",  // WGSL keyword
-                                         "bool1",
-                                         "bool1x1",
-                                         "bool1x2",
-                                         "bool1x3",
-                                         "bool1x4",
-                                         "bool2",
-                                         "bool2x1",
-                                         "bool2x2",
-                                         "bool2x3",
-                                         "bool2x4",
-                                         "bool3",
-                                         "bool3x1",
-                                         "bool3x2",
-                                         "bool3x3",
-                                         "bool3x4",
-                                         "bool4",
-                                         "bool4x1",
-                                         "bool4x2",
-                                         "bool4x3",
-                                         "bool4x4",
-                                         "branch",
-                                         // "break",  // WGSL keyword
-                                         // "call",  // WGSL builtin
-                                         // "case",  // WGSL keyword
-                                         // "catch",  // Also reserved in WGSL
-                                         "cbuffer",
-                                         // "ceil",  // WGSL builtin
-                                         "centroid",
-                                         "char",
-                                         // "clamp",  // WGSL builtin
-                                         // "class",  // Also reserved in WGSL
-                                         "clip",
-                                         // "column_major",  // Also reserved in WGSL
-                                         // "compile",  // Also reserved in WGSL
-                                         // "compile_fragment",  // Also reserved in WGSL
-                                         // "const",  // WGSL keyword
-                                         // "const_cast",  // Also reserved in WGSL
-                                         // "continue",  // WGSL keyword
-                                         // "cos",  // WGSL builtin
-                                         // "cosh",  // WGSL builtin
-                                         "countbits",
-                                         // "cross",  // WGSL builtin
-                                         "ddx",
-                                         "ddx_coarse",
-                                         "ddx_fine",
-                                         "ddy",
-                                         "ddy_coarse",
-                                         "ddy_fine",
-                                         // "default",  // WGSL keyword
-                                         "degrees",
-                                         // "delete",  // Also reserved in WGSL
-                                         // "determinant",  // WGSL builtin
-                                         // "discard",  // WGSL keyword
-                                         // "distance",  // WGSL builtin
-                                         // "do",  // WGSL keyword
-                                         // "dot",  // WGSL builtin
-                                         "double",
-                                         "double1",
-                                         "double1x1",
-                                         "double1x2",
-                                         "double1x3",
-                                         "double1x4",
-                                         "double2",
-                                         "double2x1",
-                                         "double2x2",
-                                         "double2x3",
-                                         "double2x4",
-                                         "double3",
-                                         "double3x1",
-                                         "double3x2",
-                                         "double3x3",
-                                         "double3x4",
-                                         "double4",
-                                         "double4x1",
-                                         "double4x2",
-                                         "double4x3",
-                                         "double4x4",
-                                         "dst",
-                                         "dword",
-                                         "dword1",
-                                         "dword1x1",
-                                         "dword1x2",
-                                         "dword1x3",
-                                         "dword1x4",
-                                         "dword2",
-                                         "dword2x1",
-                                         "dword2x2",
-                                         "dword2x3",
-                                         "dword2x4",
-                                         "dword3",
-                                         "dword3x1",
-                                         "dword3x2",
-                                         "dword3x3",
-                                         "dword3x4",
-                                         "dword4",
-                                         "dword4x1",
-                                         "dword4x2",
-                                         "dword4x3",
-                                         "dword4x4",
-                                         // "dynamic_cast",  // Also reserved in WGSL
-                                         // "else",  // WGSL keyword
-                                         // "enum",  // WGSL keyword
-                                         "errorf",
-                                         // "exp",  // WGSL builtin
-                                         // "exp2",  // WGSL builtin
-                                         // "explicit",  // Also reserved in WGSL
-                                         // "export",  // Also reserved in WGSL
-                                         // "extern",  // Also reserved in WGSL
-                                         "f16to32",
-                                         "f32tof16",
-                                         // "faceforward",  // WGSL builtin
-                                         // "false",  // WGSL keyword
-                                         "fastopt",
-                                         "firstbithigh",
-                                         "firstbitlow",
-                                         "flatten",
-                                         "float",
-                                         "float1",
-                                         "float1x1",
-                                         "float1x2",
-                                         "float1x3",
-                                         "float1x4",
-                                         "float2",
-                                         "float2x1",
-                                         "float2x2",
-                                         "float2x3",
-                                         "float2x4",
-                                         "float3",
-                                         "float3x1",
-                                         "float3x2",
-                                         "float3x3",
-                                         "float3x4",
-                                         "float4",
-                                         "float4x1",
-                                         "float4x2",
-                                         "float4x3",
-                                         "float4x4",
-                                         // "floor",  // WGSL builtin
-                                         // "fma",  // WGSL builtin
-                                         "fmod",
-                                         // "for",  // WGSL keyword
-                                         "forcecase",
-                                         "frac",
-                                         // "frexp",  // WGSL builtin
-                                         // "friend",  // Also reserved in WGSL
-                                         // "fwidth",  // WGSL builtin
-                                         // "fxgroup",  // Also reserved in WGSL
-                                         // "goto",  // Also reserved in WGSL
-                                         // "groupshared",  // Also reserved in WGSL
-                                         "half",
-                                         "half1",
-                                         "half1x1",
-                                         "half1x2",
-                                         "half1x3",
-                                         "half1x4",
-                                         "half2",
-                                         "half2x1",
-                                         "half2x2",
-                                         "half2x3",
-                                         "half2x4",
-                                         "half3",
-                                         "half3x1",
-                                         "half3x2",
-                                         "half3x3",
-                                         "half3x4",
-                                         "half4",
-                                         "half4x1",
-                                         "half4x2",
-                                         "half4x3",
-                                         "half4x4",
-                                         // "if",  // WGSL keyword
-                                         // "in",  // WGSL keyword
-                                         // "inline",  // Also reserved in WGSL
-                                         // "inout",  // Also reserved in WGSL
-                                         "int",
-                                         "int1",
-                                         "int1x1",
-                                         "int1x2",
-                                         "int1x3",
-                                         "int1x4",
-                                         "int2",
-                                         "int2x1",
-                                         "int2x2",
-                                         "int2x3",
-                                         "int2x4",
-                                         "int3",
-                                         "int3x1",
-                                         "int3x2",
-                                         "int3x3",
-                                         "int3x4",
-                                         "int4",
-                                         "int4x1",
-                                         "int4x2",
-                                         "int4x3",
-                                         "int4x4",
-                                         // "interface",  // Also reserved in WGSL
-                                         "isfinite",
-                                         "isinf",
-                                         "isnan",
-                                         // "ldexp",  // WGSL builtin
-                                         // "length",  // WGSL builtin
-                                         "lerp",
-                                         // "line",  // Also reserved in WGSL
-                                         // "lineadj",  // Also reserved in WGSL
-                                         "linear",
-                                         "lit",
-                                         // "log",  // WGSL builtin
-                                         "log10",
-                                         // "log2",  // WGSL builtin
-                                         "long",
-                                         // "loop",  // WGSL keyword
-                                         "mad",
-                                         "matrix",
-                                         // "max",  // WGSL builtin
-                                         // "min",  // WGSL builtin
-                                         "min10float",
-                                         "min10float1",
-                                         "min10float1x1",
-                                         "min10float1x2",
-                                         "min10float1x3",
-                                         "min10float1x4",
-                                         "min10float2",
-                                         "min10float2x1",
-                                         "min10float2x2",
-                                         "min10float2x3",
-                                         "min10float2x4",
-                                         "min10float3",
-                                         "min10float3x1",
-                                         "min10float3x2",
-                                         "min10float3x3",
-                                         "min10float3x4",
-                                         "min10float4",
-                                         "min10float4x1",
-                                         "min10float4x2",
-                                         "min10float4x3",
-                                         "min10float4x4",
-                                         "min12int",
-                                         "min12int1",
-                                         "min12int1x1",
-                                         "min12int1x2",
-                                         "min12int1x3",
-                                         "min12int1x4",
-                                         "min12int2",
-                                         "min12int2x1",
-                                         "min12int2x2",
-                                         "min12int2x3",
-                                         "min12int2x4",
-                                         "min12int3",
-                                         "min12int3x1",
-                                         "min12int3x2",
-                                         "min12int3x3",
-                                         "min12int3x4",
-                                         "min12int4",
-                                         "min12int4x1",
-                                         "min12int4x2",
-                                         "min12int4x3",
-                                         "min12int4x4",
-                                         "min16float",
-                                         "min16float1",
-                                         "min16float1x1",
-                                         "min16float1x2",
-                                         "min16float1x3",
-                                         "min16float1x4",
-                                         "min16float2",
-                                         "min16float2x1",
-                                         "min16float2x2",
-                                         "min16float2x3",
-                                         "min16float2x4",
-                                         "min16float3",
-                                         "min16float3x1",
-                                         "min16float3x2",
-                                         "min16float3x3",
-                                         "min16float3x4",
-                                         "min16float4",
-                                         "min16float4x1",
-                                         "min16float4x2",
-                                         "min16float4x3",
-                                         "min16float4x4",
-                                         "min16int",
-                                         "min16int1",
-                                         "min16int1x1",
-                                         "min16int1x2",
-                                         "min16int1x3",
-                                         "min16int1x4",
-                                         "min16int2",
-                                         "min16int2x1",
-                                         "min16int2x2",
-                                         "min16int2x3",
-                                         "min16int2x4",
-                                         "min16int3",
-                                         "min16int3x1",
-                                         "min16int3x2",
-                                         "min16int3x3",
-                                         "min16int3x4",
-                                         "min16int4",
-                                         "min16int4x1",
-                                         "min16int4x2",
-                                         "min16int4x3",
-                                         "min16int4x4",
-                                         "min16uint",
-                                         "min16uint1",
-                                         "min16uint1x1",
-                                         "min16uint1x2",
-                                         "min16uint1x3",
-                                         "min16uint1x4",
-                                         "min16uint2",
-                                         "min16uint2x1",
-                                         "min16uint2x2",
-                                         "min16uint2x3",
-                                         "min16uint2x4",
-                                         "min16uint3",
-                                         "min16uint3x1",
-                                         "min16uint3x2",
-                                         "min16uint3x3",
-                                         "min16uint3x4",
-                                         "min16uint4",
-                                         "min16uint4x1",
-                                         "min16uint4x2",
-                                         "min16uint4x3",
-                                         "min16uint4x4",
-                                         // "modf",  // WGSL builtin
-                                         "msad4",
-                                         "mul",
-                                         // "mutable",  // Also reserved in WGSL
-                                         // "namespace",  // Also reserved in WGSL
-                                         // "new",  // Also reserved in WGSL
-                                         // "nointerpolation",  // Also reserved in WGSL
-                                         "noise",
-                                         // "noperspective",  // Also reserved in WGSL
-                                         // "normalize",  // WGSL builtin
-                                         "numthreads",
-                                         // "operator",  // Also reserved in WGSL
-                                         // "out",  // WGSL keyword
-                                         // "packoffset",  // Also reserved in WGSL
-                                         // "pass",  // Also reserved in WGSL
-                                         // "pixelfragment",  // Also reserved in WGSL
-                                         "pixelshader",
-                                         // "point",  // Also reserved in WGSL
-                                         // "pow",  // WGSL builtin
-                                         // "precise",  // Also reserved in WGSL
-                                         "printf",
-                                         // "private",  // WGSL keyword
-                                         // "protected",  // Also reserved in WGSL
-                                         // "public",  // Also reserved in WGSL
-                                         "radians",
-                                         "rcp",
-                                         // "reflect",  // WGSL builtin
-                                         "refract",
-                                         // "register",  // Also reserved in WGSL
-                                         // "reinterpret_cast",  // Also reserved in WGSL
-                                         // "return",  // WGSL keyword
-                                         // "reversebits",  // WGSL builtin
-                                         // "round",  // WGSL builtin
-                                         "row_major",
-                                         "rsqrt",
-                                         "sample",
-                                         "sampler1D",
-                                         "sampler2D",
-                                         "sampler3D",
-                                         "samplerCUBE",
-                                         "sampler_state",
-                                         "saturate",
-                                         // "shared",  // Also reserved in WGSL
-                                         "short",
-                                         // "sign",  // WGSL builtin
-                                         // "signed",  // Also reserved in WGSL
-                                         // "sin",  // WGSL builtin
-                                         "sincos",
-                                         // "sinh",  // WGSL builtin
-                                         // "sizeof",  // Also reserved in WGSL
-                                         // "smoothstep",  // WGSL builtin
-                                         // "snorm",  // Also reserved in WGSL
-                                         // "sqrt",  // WGSL builtin
-                                         "stateblock",
-                                         "stateblock_state",
-                                         // "static",  // Also reserved in WGSL
-                                         // "static_cast",  // Also reserved in WGSL
-                                         // "step",  // WGSL builtin
-                                         "string",
-                                         // "struct",  // WGSL keyword
-                                         // "switch",  // WGSL keyword
-                                         // "tan",  // WGSL builtin
-                                         // "tanh",  // WGSL builtin
-                                         "tbuffer",
-                                         "technique",
-                                         "technique10",
-                                         "technique11",
-                                         // "template",  // Also reserved in WGSL
-                                         "tex1D",
-                                         "tex1Dbias",
-                                         "tex1Dgrad",
-                                         "tex1Dlod",
-                                         "tex1Dproj",
-                                         "tex2D",
-                                         "tex2Dbias",
-                                         "tex2Dgrad",
-                                         "tex2Dlod",
-                                         "tex2Dproj",
-                                         "tex3D",
-                                         "tex3Dbias",
-                                         "tex3Dgrad",
-                                         "tex3Dlod",
-                                         "tex3Dproj",
-                                         "texCUBE",
-                                         "texCUBEbias",
-                                         "texCUBEgrad",
-                                         "texCUBElod",
-                                         "texCUBEproj",
-                                         "texture",
-                                         "texture1D",
-                                         "texture1DArray",
-                                         "texture2D",
-                                         "texture2DArray",
-                                         "texture2DMS",
-                                         "texture2DMSArray",
-                                         "texture3D",
-                                         "textureCube",
-                                         "textureCubeArray",
-                                         // "this",  // Also reserved in WGSL
-                                         // "throw",  // Also reserved in WGSL
-                                         "transpose",
-                                         "triangle",
-                                         "triangleadj",
-                                         // "true",  // WGSL keyword
-                                         // "trunc",  // WGSL builtin
-                                         // "try",  // Also reserved in WGSL
-                                         // "typedef",  // WGSL keyword
-                                         // "typename",  // Also reserved in WGSL
-                                         "uint",
-                                         "uint1",
-                                         "uint1x1",
-                                         "uint1x2",
-                                         "uint1x3",
-                                         "uint1x4",
-                                         "uint2",
-                                         "uint2x1",
-                                         "uint2x2",
-                                         "uint2x3",
-                                         "uint2x4",
-                                         "uint3",
-                                         "uint3x1",
-                                         "uint3x2",
-                                         "uint3x3",
-                                         "uint3x4",
-                                         "uint4",
-                                         "uint4x1",
-                                         "uint4x2",
-                                         "uint4x3",
-                                         "uint4x4",
-                                         // "uniform",  // WGSL keyword
-                                         // "union",  // Also reserved in WGSL
-                                         // "unorm",  // Also reserved in WGSL
-                                         "unroll",
-                                         "unsigned",
-                                         // "using",  // WGSL reserved keyword
-                                         "vector",
-                                         "vertexfragment",
-                                         "vertexshader",
-                                         // "virtual",  // Also reserved in WGSL
-                                         // "void",  // WGSL keyword
-                                         // "volatile",  // Also reserved in WGSL
-                                         // "while"  // WGSL reserved keyword
-                                         kUnicodeIdentifier));
-
-INSTANTIATE_TEST_SUITE_P(
-    RenamerTestMsl,
-    RenamerTestMsl,
-    testing::Values(
-        // c++14 spec
-        // "alignas",  // Also reserved in WGSL
-        // "alignof",  // Also reserved in WGSL
-        "and",
-        "and_eq",
-        // "asm",  // Also reserved in WGSL
-        // "auto",  // Also reserved in WGSL
-        "bitand",
-        "bitor",
-        // "bool",   // Also used in WGSL
-        // "break",  // Also used in WGSL
-        // "case",   // Also used in WGSL
-        // "catch",  // Also reserved in WGSL
-        "char",
-        "char16_t",
-        "char32_t",
-        // "class",  // Also reserved in WGSL
-        "compl",
-        // "const",     // Also used in WGSL
-        // "const_cast",  // Also reserved in WGSL
-        // "constexpr",  // Also reserved in WGSL
-        // "continue",  // Also used in WGSL
-        // "decltype",  // Also reserved in WGSL
-        // "default",   // Also used in WGSL
-        // "delete",  // Also reserved in WGSL
-        // "do",  // Also used in WGSL
-        "double",
-        // "dynamic_cast",  // Also reserved in WGSL
-        // "else",  // Also used in WGSL
-        // "enum",  // Also used in WGSL
-        // "explicit",  // Also reserved in WGSL
-        // "extern",  // Also reserved in WGSL
-        // "false",  // Also used in WGSL
-        // "final",  // Also reserved in WGSL
-        "float",
-        // "for",  // Also used in WGSL
-        // "friend",  // Also reserved in WGSL
-        // "goto",  // Also reserved in WGSL
-        // "if",  // Also used in WGSL
-        // "inline",  // Also reserved in WGSL
-        "int",
-        "long",
-        // "mutable",  // Also reserved in WGSL
-        // "namespace",  // Also reserved in WGSL
-        // "new",  // Also reserved in WGSL
-        // "noexcept",  // Also reserved in WGSL
-        "not",
-        "not_eq",
-        // "nullptr",  // Also reserved in WGSL
-        // "operator",  // Also reserved in WGSL
-        "or",
-        "or_eq",
-        // "override", // Also used in WGSL
-        // "private",  // Also used in WGSL
-        // "protected",  // Also reserved in WGSL
-        // "public",  // Also reserved in WGSL
-        // "register",  // Also reserved in WGSL
-        // "reinterpret_cast",  // Also reserved in WGSL
-        // "return",  // Also used in WGSL
-        "short",
-        // "signed",  // Also reserved in WGSL
-        // "sizeof",  // Also reserved in WGSL
-        // "static",  // Also reserved in WGSL
-        // "static_assert",  // Also reserved in WGSL
-        // "static_cast",  // Also reserved in WGSL
-        // "struct",  // Also used in WGSL
-        // "switch",  // Also used in WGSL
-        // "template",  // Also reserved in WGSL
-        // "this",  // Also reserved in WGSL
-        // "thread_local",  // Also reserved in WGSL
-        // "throw",  // Also reserved in WGSL
-        // "true",  // Also used in WGSL
-        // "try",  // Also reserved in WGSL
-        // "typedef",  // Also used in WGSL
-        // "typeid",  // Also reserved in WGSL
-        // "typename",  // Also reserved in WGSL
-        // "union",  // Also reserved in WGSL
-        "unsigned",
-        // "using",  // WGSL reserved keyword
-        // "virtual",  // Also reserved in WGSL
-        // "void",  // Also used in WGSL
-        // "volatile",  // Also reserved in WGSL
-        "wchar_t",
-        // "while",  // WGSL reserved keyword
-        "xor",
-        "xor_eq",
-
-        // Metal Spec
-        "access",
-        // "array",  // Also used in WGSL
-        "array_ref",
-        "as_type",
-        // "atomic",  // Also used in WGSL
-        "atomic_bool",
-        "atomic_int",
-        "atomic_uint",
-        "bool2",
-        "bool3",
-        "bool4",
-        "buffer",
-        "char2",
-        "char3",
-        "char4",
-        "const_reference",
-        "constant",
-        "depth2d",
-        "depth2d_array",
-        "depth2d_ms",
-        "depth2d_ms_array",
-        "depthcube",
-        "depthcube_array",
-        "device",
-        "discard_fragment",
-        "float2",
-        "float2x2",
-        "float2x3",
-        "float2x4",
-        "float3",
-        "float3x2",
-        "float3x3",
-        "float3x4",
-        "float4",
-        "float4x2",
-        "float4x3",
-        "float4x4",
-        "fragment",
-        "half",
-        "half2",
-        "half2x2",
-        "half2x3",
-        "half2x4",
-        "half3",
-        "half3x2",
-        "half3x3",
-        "half3x4",
-        "half4",
-        "half4x2",
-        "half4x3",
-        "half4x4",
-        "imageblock",
-        "int16_t",
-        "int2",
-        "int3",
-        "int32_t",
-        "int4",
-        "int64_t",
-        "int8_t",
-        "kernel",
-        "long2",
-        "long3",
-        "long4",
-        "main",  // No functions called main
-        "matrix",
-        "metal",  // The namespace
-        "packed_bool2",
-        "packed_bool3",
-        "packed_bool4",
-        "packed_char2",
-        "packed_char3",
-        "packed_char4",
-        "packed_float2",
-        "packed_float3",
-        "packed_float4",
-        "packed_half2",
-        "packed_half3",
-        "packed_half4",
-        "packed_int2",
-        "packed_int3",
-        "packed_int4",
-        "packed_short2",
-        "packed_short3",
-        "packed_short4",
-        "packed_uchar2",
-        "packed_uchar3",
-        "packed_uchar4",
-        "packed_uint2",
-        "packed_uint3",
-        "packed_uint4",
-        "packed_ushort2",
-        "packed_ushort3",
-        "packed_ushort4",
-        "patch_control_point",
-        "ptrdiff_t",
-        "r16snorm",
-        "r16unorm",
-        "r8unorm",
-        "reference",
-        "rg11b10f",
-        "rg16snorm",
-        "rg16unorm",
-        "rg8snorm",
-        "rg8unorm",
-        "rgb10a2",
-        "rgb9e5",
-        "rgba16snorm",
-        "rgba16unorm",
-        "rgba8snorm",
-        "rgba8unorm",
-        // "sampler",  // Also used in WGSL
-        "short2",
-        "short3",
-        "short4",
-        "size_t",
-        "srgba8unorm",
-        "texture",
-        "texture1d",
-        "texture1d_array",
-        "texture2d",
-        "texture2d_array",
-        "texture2d_ms",
-        "texture2d_ms_array",
-        "texture3d",
-        "texture_buffer",
-        "texturecube",
-        "texturecube_array",
-        "thread",
-        "threadgroup",
-        "threadgroup_imageblock",
-        "uchar",
-        "uchar2",
-        "uchar3",
-        "uchar4",
-        "uint",
-        "uint16_t",
-        "uint2",
-        "uint3",
-        "uint32_t",
-        "uint4",
-        "uint64_t",
-        "uint8_t",
-        "ulong2",
-        "ulong3",
-        "ulong4",
-        // "uniform",  // Also used in WGSL
-        "ushort",
-        "ushort2",
-        "ushort3",
-        "ushort4",
-        // "vec",  // WGSL reserved keyword
-        "vertex",
-
-        // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
-        // Table 6.5. Constants for single-precision floating-point math
-        // functions
-        "MAXFLOAT",
-        "HUGE_VALF",
-        "INFINITY",
-        "infinity",
-        "NAN",
-        "M_E_F",
-        "M_LOG2E_F",
-        "M_LOG10E_F",
-        "M_LN2_F",
-        "M_LN10_F",
-        "M_PI_F",
-        "M_PI_2_F",
-        "M_PI_4_F",
-        "M_1_PI_F",
-        "M_2_PI_F",
-        "M_2_SQRTPI_F",
-        "M_SQRT2_F",
-        "M_SQRT1_2_F",
-        "MAXHALF",
-        "HUGE_VALH",
-        "M_E_H",
-        "M_LOG2E_H",
-        "M_LOG10E_H",
-        "M_LN2_H",
-        "M_LN10_H",
-        "M_PI_H",
-        "M_PI_2_H",
-        "M_PI_4_H",
-        "M_1_PI_H",
-        "M_2_PI_H",
-        "M_2_SQRTPI_H",
-        "M_SQRT2_H",
-        "M_SQRT1_2_H",
-        // "while"  // WGSL reserved keyword
-        kUnicodeIdentifier));
-
-std::string ExpandBuiltinType(std::string_view name) {
-    if (name == "array") {
-        return "array<i32, 4>";
-    }
-    if (name == "atomic") {
-        return "atomic<i32>";
-    }
-    if (name == "mat2x2") {
-        return "mat2x2<f32>";
-    }
-    if (name == "mat2x2f") {
-        return "mat2x2<f32>";
-    }
-    if (name == "mat2x2h") {
-        return "mat2x2<f16>";
-    }
-    if (name == "mat2x3") {
-        return "mat2x3<f32>";
-    }
-    if (name == "mat2x3f") {
-        return "mat2x3<f32>";
-    }
-    if (name == "mat2x3h") {
-        return "mat2x3<f16>";
-    }
-    if (name == "mat2x4") {
-        return "mat2x4<f32>";
-    }
-    if (name == "mat2x4f") {
-        return "mat2x4<f32>";
-    }
-    if (name == "mat2x4h") {
-        return "mat2x4<f16>";
-    }
-    if (name == "mat3x2") {
-        return "mat3x2<f32>";
-    }
-    if (name == "mat3x2f") {
-        return "mat3x2<f32>";
-    }
-    if (name == "mat3x2h") {
-        return "mat3x2<f16>";
-    }
-    if (name == "mat3x3") {
-        return "mat3x3<f32>";
-    }
-    if (name == "mat3x3f") {
-        return "mat3x3<f32>";
-    }
-    if (name == "mat3x3h") {
-        return "mat3x3<f16>";
-    }
-    if (name == "mat3x4") {
-        return "mat3x4<f32>";
-    }
-    if (name == "mat3x4f") {
-        return "mat3x4<f32>";
-    }
-    if (name == "mat3x4h") {
-        return "mat3x4<f16>";
-    }
-    if (name == "mat4x2") {
-        return "mat4x2<f32>";
-    }
-    if (name == "mat4x2f") {
-        return "mat4x2<f32>";
-    }
-    if (name == "mat4x2h") {
-        return "mat4x2<f16>";
-    }
-    if (name == "mat4x3") {
-        return "mat4x3<f32>";
-    }
-    if (name == "mat4x3f") {
-        return "mat4x3<f32>";
-    }
-    if (name == "mat4x3h") {
-        return "mat4x3<f16>";
-    }
-    if (name == "mat4x4") {
-        return "mat4x4<f32>";
-    }
-    if (name == "mat4x4f") {
-        return "mat4x4<f32>";
-    }
-    if (name == "mat4x4h") {
-        return "mat4x4<f16>";
-    }
-    if (name == "ptr") {
-        return "ptr<function, i32>";
-    }
-    if (name == "vec2") {
-        return "vec2<f32>";
-    }
-    if (name == "vec2f") {
-        return "vec2<f32>";
-    }
-    if (name == "vec2h") {
-        return "vec2<f16>";
-    }
-    if (name == "vec2i") {
-        return "vec2<i32>";
-    }
-    if (name == "vec2u") {
-        return "vec2<u32>";
-    }
-    if (name == "vec3") {
-        return "vec3<f32>";
-    }
-    if (name == "vec3f") {
-        return "vec3<f32>";
-    }
-    if (name == "vec3h") {
-        return "vec3<f16>";
-    }
-    if (name == "vec3i") {
-        return "vec3<i32>";
-    }
-    if (name == "vec3u") {
-        return "vec3<u32>";
-    }
-    if (name == "vec4") {
-        return "vec4<f32>";
-    }
-    if (name == "vec4f") {
-        return "vec4<f32>";
-    }
-    if (name == "vec4h") {
-        return "vec4<f16>";
-    }
-    if (name == "vec4i") {
-        return "vec4<i32>";
-    }
-    if (name == "vec4u") {
-        return "vec4<u32>";
-    }
-    return std::string(name);
-}
-
-std::vector<std::string_view> ConstructableTypes() {
-    std::vector<std::string_view> out;
-    for (auto type : core::kBuiltinTypeStrings) {
-        if (type != "ptr" && type != "atomic" && !tint::HasPrefix(type, "sampler") &&
-            !tint::HasPrefix(type, "texture") && !tint::HasPrefix(type, "__") &&
-            !tint::HasPrefix(type, "input_attachment") &&
-            !tint::HasPrefix(type, "subgroup_matrix") && !tint::HasPrefix(type, "binding_array") &&
-            type != "i8" && type != "u8") {
-            out.push_back(type);
-        }
-    }
-    return out;
-}
-
-using RenamerBuiltinTypeTest = TransformTestWithParam<std::string_view>;
-
-TEST_P(RenamerBuiltinTypeTest, PreserveTypeUsage) {
-    auto expand = [&](std::string_view source) {
-        return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
-    };
-
-    auto src = expand(R"(
-enable f16;
-
-fn x(v : $type) -> $type {
-  const a : $type = $type();
-  let b : $type = a;
-  var c : $type = b;
-  return c;
-}
-
-struct y {
-  a : $type,
-}
-)");
-
-    auto expect = expand(R"(
-enable f16;
-
-fn tint_symbol(tint_symbol_1 : $type) -> $type {
-  const tint_symbol_2 : $type = $type();
-  let tint_symbol_3 : $type = tint_symbol_2;
-  var tint_symbol_4 : $type = tint_symbol_3;
-  return tint_symbol_4;
-}
-
-struct tint_symbol_5 {
-  tint_symbol_2 : $type,
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-TEST_P(RenamerBuiltinTypeTest, PreserveTypeInitializer) {
-    auto expand = [&](std::string_view source) {
-        return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
-    };
-
-    auto src = expand(R"(
-enable f16;
-
-@fragment
-fn f() {
-  var v : $type = $type();
-}
-)");
-
-    auto expect = expand(R"(
-enable f16;
-
-@fragment
-fn tint_symbol() {
-  var tint_symbol_1 : $type = $type();
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinTypeTest, PreserveTypeConversion) {
-    if (std::string_view(GetParam()) == "array") {
-        return;  // Cannot value convert arrays.
-    }
-
-    auto expand = [&](std::string_view source) {
-        return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
-    };
-
-    auto src = expand(R"(
-enable f16;
-
-@fragment
-fn f() {
-  var v : $type = $type($type());
-}
-)");
-
-    auto expect = expand(R"(
-enable f16;
-
-@fragment
-fn tint_symbol() {
-  var tint_symbol_1 : $type = $type($type());
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(RenamerBuiltinTypeTest, PreserveTypeExpression) {
-    auto src = R"(
-enable f16;
-
-@fragment
-fn f() {
-  var v : array<f32, 2> = array<f32, 2>();
-}
-)";
-
-    auto expect = R"(
-enable f16;
-
-@fragment
-fn tint_symbol() {
-  var tint_symbol_1 : array<f32, 2> = array<f32, 2>();
-}
-)";
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinTypeTest, RenameShadowedByAlias) {
-    auto expand = [&](std::string_view source) {
-        std::string_view ty = GetParam();
-        auto out = tint::ReplaceAll(source, "$name", ty);
-        out = tint::ReplaceAll(out, "$type", ExpandBuiltinType(ty));
-        out = tint::ReplaceAll(out, "$other_type", ty == "i32" ? "u32" : "i32");
-        return out;
-    };
-
-    auto src = expand(R"(
-alias $name = $other_type;
-
-@fragment
-fn f() {
-  var v : $other_type = $name();
-}
-)");
-
-    auto expect = expand(R"(
-alias tint_symbol = $other_type;
-
-@fragment
-fn tint_symbol_1() {
-  var tint_symbol_2 : $other_type = tint_symbol();
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinTypeTest, RenameShadowedByStruct) {
-    auto expand = [&](std::string_view source) {
-        std::string_view ty = GetParam();
-        auto out = tint::ReplaceAll(source, "$name", ty);
-        out = tint::ReplaceAll(out, "$type", ExpandBuiltinType(ty));
-        out = tint::ReplaceAll(out, "$other_type", ty == "i32" ? "u32" : "i32");
-        return out;
-    };
-
-    auto src = expand(R"(
-struct $name {
-  i : $other_type,
-}
-
-@fragment
-fn f() {
-  var a = $name();
-  var b = a.i;
-}
-)");
-
-    auto expect = expand(R"(
-struct tint_symbol {
-  tint_symbol_1 : $other_type,
-}
-
-@fragment
-fn tint_symbol_2() {
-  var tint_symbol_3 = tint_symbol();
-  var tint_symbol_4 = tint_symbol_3.tint_symbol_1;
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(RenamerBuiltinTypeTest,
-                         RenamerBuiltinTypeTest,
-                         testing::ValuesIn(ConstructableTypes()));
-
-/// @return WGSL builtin identifier keywords
-std::vector<std::string_view> Identifiers() {
-    std::vector<std::string_view> out;
-    for (auto ident : core::kBuiltinTypeStrings) {
-        if (!tint::HasPrefix(ident, "__")) {
-            out.push_back(ident);
-        }
-    }
-    for (auto ident : core::kAddressSpaceStrings) {
-        if (!tint::HasPrefix(ident, "_")) {
-            out.push_back(ident);
-        }
-    }
-    for (auto ident : core::kTexelFormatStrings) {
-        out.push_back(ident);
-    }
-    for (auto ident : core::kAccessStrings) {
-        out.push_back(ident);
-    }
-    return out;
-}
-
-using RenamerBuiltinIdentifierTest = TransformTestWithParam<std::string_view>;
-
-TEST_P(RenamerBuiltinIdentifierTest, GlobalConstName) {
-    auto expand = [&](std::string_view source) {
-        return tint::ReplaceAll(source, "$name", GetParam());
-    };
-
-    auto src = expand(R"(
-const $name = 42;
-
-fn f() {
-  const v = $name;
-}
-)");
-
-    auto expect = expand(R"(
-const tint_symbol = 42;
-
-fn tint_symbol_1() {
-  const tint_symbol_2 = tint_symbol;
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinIdentifierTest, LocalVarName) {
-    auto expand = [&](std::string_view source) {
-        return tint::ReplaceAll(source, "$name", GetParam());
-    };
-
-    auto src = expand(R"(
-fn f() {
-  var $name = 42;
-}
-)");
-
-    auto expect = expand(R"(
-fn tint_symbol() {
-  var tint_symbol_1 = 42;
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinIdentifierTest, FunctionName) {
-    auto expand = [&](std::string_view source) {
-        return tint::ReplaceAll(source, "$name", GetParam());
-    };
-
-    auto src = expand(R"(
-fn $name() {
-}
-
-fn f() {
-  $name();
-}
-)");
-
-    auto expect = expand(R"(
-fn tint_symbol() {
-}
-
-fn tint_symbol_1() {
-  tint_symbol();
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RenamerBuiltinIdentifierTest, StructName) {
-    auto expand = [&](std::string_view source) {
-        std::string_view name = GetParam();
-        auto out = tint::ReplaceAll(source, "$name", name);
-        return tint::ReplaceAll(out, "$other_type", name == "i32" ? "u32" : "i32");
-    };
-
-    auto src = expand(R"(
-struct $name {
-  i : $other_type,
-}
-
-fn f() {
-  var x = $name();
-}
-)");
-
-    auto expect = expand(R"(
-struct tint_symbol {
-  tint_symbol_1 : $other_type,
-}
-
-fn tint_symbol_2() {
-  var tint_symbol_3 = tint_symbol();
-}
-)");
-
-    auto got = Run<Renamer>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(RenamerBuiltinIdentifierTest,
-                         RenamerBuiltinIdentifierTest,
-                         testing::ValuesIn(Identifiers()));
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/robustness.cc b/src/tint/lang/wgsl/ast/transform/robustness.cc
deleted file mode 100644
index 09c4d5a..0000000
--- a/src/tint/lang/wgsl/ast/transform/robustness.cc
+++ /dev/null
@@ -1,743 +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/transform/robustness.h"
-
-#include <algorithm>
-#include <limits>
-#include <utility>
-
-#include "src/tint/lang/core/type/memory_view.h"
-#include "src/tint/lang/core/type/reference.h"
-#include "src/tint/lang/wgsl/ast/transform/hoist_to_decl_before.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/block_statement.h"
-#include "src/tint/lang/wgsl/sem/builtin_fn.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/lang/wgsl/sem/function.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/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Robustness);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Robustness::Config);
-
-namespace tint::ast::transform {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-/// PIMPL state for the transform
-struct Robustness::State {
-    /// Constructor
-    /// @param p the source program
-    /// @param c the transform config
-    State(const Program& p, Config&& c) : src(p), cfg(std::move(c)) {}
-
-    /// Runs the transform
-    /// @returns the new program or SkipTransform if the transform is not required
-    ApplyResult Run() {
-        if (HasAction(Action::kPredicate)) {
-            AddPredicateParameters();
-        }
-
-        // Walk all the AST nodes in the module, starting with the leaf nodes.
-        // The most deeply nested expressions will come first.
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
-            Switch(
-                node,  //
-                [&](const IndexAccessorExpression* e) {
-                    // obj[idx]
-                    // Array, matrix and vector indexing may require robustness transformation.
-                    auto* expr = sem.Get(e)->Unwrap()->As<sem::IndexAccessorExpression>();
-                    if (IsIgnoredResourceBinding(expr->Object()->RootIdentifier())) {
-                        return;
-                    }
-                    if (cfg.disable_runtime_sized_array_index_clamping &&
-                        IsIndexAccessingRuntimeSizedArray(expr)) {
-                        // Ensure the index is always u32 as using a negative index is an undefined
-                        // behavior in SPIRV.
-                        auto* idx = CastToU32(expr->Index());
-                        ctx.Replace(expr->Declaration()->index, idx);
-                        return;
-                    }
-                    switch (ActionFor(expr)) {
-                        case Action::kPredicate:
-                            PredicateIndexAccessor(expr);
-                            break;
-                        case Action::kClamp:
-                            ClampIndexAccessor(expr);
-                            break;
-                        case Action::kIgnore:
-                            break;
-                    }
-                },
-                [&](const IdentifierExpression* e) {
-                    // Identifiers may resolve to pointer lets, which may be predicated.
-                    // Inspect.
-                    if (auto* user = sem.Get<sem::VariableUser>(e)) {
-                        auto* v = user->Variable();
-                        if (v->Type()->Is<core::type::Pointer>()) {
-                            // Propagate predicate from pointer
-                            if (auto pred = predicates.Get(v->Declaration()->initializer)) {
-                                predicates.Add(e, *pred);
-                            }
-                        }
-                    }
-                },
-                [&](const AccessorExpression* e) {
-                    // obj.member
-                    // Propagate the predication from the object to this expression.
-                    if (auto pred = predicates.Get(e->object)) {
-                        predicates.Add(e, *pred);
-                    }
-                },
-                [&](const UnaryOpExpression* e) {
-                    // Includes address-of, or indirection
-                    // Propagate the predication from the inner expression to this expression.
-                    if (auto pred = predicates.Get(e->expr)) {
-                        predicates.Add(e, *pred);
-                    }
-                },
-                [&](const AssignmentStatement* s) {
-                    if (auto pred = predicates.Get(s->lhs)) {
-                        // Assignment target is predicated
-                        // Replace statement with condition on the predicate
-                        ctx.Replace(s, b.If(*pred, b.Block(ctx.Clone(s))));
-                    }
-                },
-                [&](const CompoundAssignmentStatement* s) {
-                    if (auto pred = predicates.Get(s->lhs)) {
-                        // Assignment expression is predicated
-                        // Replace statement with condition on the predicate
-                        ctx.Replace(s, b.If(*pred, b.Block(ctx.Clone(s))));
-                    }
-                },
-                [&](const IncrementDecrementStatement* s) {
-                    if (auto pred = predicates.Get(s->lhs)) {
-                        // Assignment expression is predicated
-                        // Replace statement with condition on the predicate
-                        ctx.Replace(s, b.If(*pred, b.Block(ctx.Clone(s))));
-                    }
-                },
-                [&](const CallExpression* e) {
-                    if (auto* call = sem.Get<sem::Call>(e)) {
-                        Switch(
-                            call->Target(),  //
-                            [&](const sem::BuiltinFn* builtin) {
-                                // Calls to builtins may require robustness transformation.
-                                // Inspect.
-                                if (builtin->IsTexture()) {
-                                    switch (cfg.texture_action) {
-                                        case Action::kPredicate:
-                                            PredicateTextureBuiltin(call, builtin);
-                                            break;
-                                        case Action::kClamp:
-                                            ClampTextureBuiltin(call, builtin);
-                                            break;
-                                        case Action::kIgnore:
-                                            break;
-                                    }
-                                } else {
-                                    MaybePredicateNonTextureBuiltin(call, builtin);
-                                }
-                            },
-                            [&](const sem::Function* fn) {
-                                // Calls to user function may require passing additional predicate
-                                // arguments.
-                                InsertPredicateArguments(call, fn);
-                            });
-                    }
-                });
-
-            // Check whether the node is an expression that:
-            // * Has a predicate
-            // * Is of a non-pointer or non-reference type
-            // If the above is true, then we need to predicate evaluation of this expression by
-            // replacing `expr` with `predicated_expr` and injecting the following above the
-            // expression's statement:
-            //
-            //   var predicated_expr : expr_ty;
-            //   if (predicate) {
-            //     predicated_expr = expr;
-            //   }
-            //
-            if (auto* expr = node->As<Expression>()) {
-                if (auto pred = predicates.Get(expr)) {
-                    // Expression is predicated
-                    auto* sem_expr = sem.GetVal(expr);
-                    if (!sem_expr->Type()->Is<core::type::MemoryView>()) {
-                        auto pred_load = b.Symbols().New("predicated_expr");
-                        auto ty = CreateASTTypeFor(ctx, sem_expr->Type());
-                        hoist.InsertBefore(sem_expr->Stmt(), b.Decl(b.Var(pred_load, ty)));
-                        hoist.InsertBefore(
-                            sem_expr->Stmt(),
-                            b.If(*pred, b.Block(b.Assign(pred_load, ctx.Clone(expr)))));
-                        ctx.Replace(expr, b.Expr(pred_load));
-
-                        // The predication has been consumed for this expression.
-                        // Don't predicate expressions that use this expression.
-                        predicates.Remove(expr);
-                    }
-                }
-            }
-        }
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-  private:
-    /// The source program
-    const Program& src;
-    /// The transform's config
-    Config cfg;
-    /// The target program builder
-    ProgramBuilder b{};
-    /// The clone context
-    program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
-    /// Helper for hoisting declarations
-    HoistToDeclBefore hoist{ctx};
-    /// Alias to the source program's semantic info
-    const sem::Info& sem = ctx.src->Sem();
-    /// Map of expression to predicate condition
-    Hashmap<const Expression*, Symbol, 32> predicates{};
-
-    /// @return the `u32` typed expression that represents the maximum indexable value for the index
-    /// accessor @p expr, or nullptr if there is no robustness limit for this expression.
-    const Expression* DynamicLimitFor(const sem::IndexAccessorExpression* expr) {
-        auto* obj_type = expr->Object()->Type();
-        return Switch(
-            obj_type->UnwrapPtrOrRef(),  //
-            [&](const core::type::Vector* vec) -> const Expression* {
-                if (expr->Index()->ConstantValue() || expr->Index()->Is<sem::Swizzle>()) {
-                    // Index and size is constant.
-                    // Validation will have rejected any OOB accesses.
-                    return nullptr;
-                }
-                return b.Expr(u32(vec->Width() - 1u));
-            },
-            [&](const core::type::Matrix* mat) -> const Expression* {
-                if (expr->Index()->ConstantValue()) {
-                    // Index and size is constant.
-                    // Validation will have rejected any OOB accesses.
-                    return nullptr;
-                }
-                return b.Expr(u32(mat->Columns() - 1u));
-            },
-            [&](const core::type::Array* arr) -> const Expression* {
-                if (arr->Count()->Is<core::type::RuntimeArrayCount>()) {
-                    // Size is unknown until runtime.
-                    // Must clamp, even if the index is constant.
-
-                    auto* arr_ptr = b.AddressOf(ctx.Clone(expr->Object()->Declaration()));
-                    return b.Sub(b.Call(wgsl::BuiltinFn::kArrayLength, arr_ptr), 1_u);
-                }
-                if (auto count = arr->ConstantCount()) {
-                    if (expr->Index()->ConstantValue()) {
-                        // Index and size is constant.
-                        // Validation will have rejected any OOB accesses.
-                        return nullptr;
-                    }
-                    return b.Expr(u32(count.value() - 1u));
-                }
-                // Note: Don't be tempted to use the array override variable as an expression here,
-                // the name might be shadowed!
-                b.Diagnostics().AddError(Source{}) << core::type::Array::kErrExpectedConstantCount;
-                return nullptr;
-            },  //
-            TINT_ICE_ON_NO_MATCH);
-    }
-
-    /// Transform the program to insert additional predicate parameters to all user functions that
-    /// have a pointer parameter type in an address space that has predicate action.
-    void AddPredicateParameters() {
-        for (auto* fn : src.AST().Functions()) {
-            for (auto* param : fn->params) {
-                auto* sem_param = sem.Get(param);
-                if (auto* ptr = sem_param->Type()->As<core::type::Pointer>()) {
-                    if (ActionFor(ptr->AddressSpace()) == Action::kPredicate) {
-                        auto name = b.Symbols().New(param->name->symbol.Name() + "_predicate");
-                        ctx.InsertAfter(fn->params, param, b.Param(name, b.ty.bool_()));
-
-                        // Associate the pointer parameter expressions with the predicate.
-                        for (auto* user : sem_param->Users()) {
-                            predicates.Add(user->Declaration(), name);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /// Transforms call expressions to user functions, inserting additional predicate arguments
-    /// after all pointer parameters with a type in an address space that has predicate action.
-    void InsertPredicateArguments(const sem::Call* call, const sem::Function* fn) {
-        auto* expr = call->Declaration();
-        for (size_t i = 0; i < fn->Parameters().Length(); i++) {
-            auto* param = fn->Parameters()[i];
-            if (auto* ptr = param->Type()->As<core::type::Pointer>()) {
-                if (ActionFor(ptr->AddressSpace()) == Action::kPredicate) {
-                    auto* arg = expr->args[i];
-                    if (auto predicate = predicates.Get(arg)) {
-                        ctx.InsertAfter(expr->args, arg, b.Expr(*predicate));
-                    } else {
-                        ctx.InsertAfter(expr->args, arg, b.Expr(true));
-                    }
-                }
-            }
-        }
-    }
-
-    /// Applies predication to the index on an array, vector or matrix.
-    /// @param expr the index accessor expression.
-    void PredicateIndexAccessor(const sem::IndexAccessorExpression* expr) {
-        auto* obj = expr->Object()->Declaration();
-        auto* idx = expr->Index()->Declaration();
-        auto* max = DynamicLimitFor(expr);
-        if (!max) {
-            // robustness is not required
-            // Just propagate predicate from object
-            if (auto pred = predicates.Get(obj)) {
-                predicates.Add(expr->Declaration(), *pred);
-            }
-            return;
-        }
-
-        auto* stmt = expr->Stmt();
-        auto& obj_pred = predicates.GetOrAddZero(obj);
-
-        auto idx_let = b.Symbols().New("index");
-        auto pred = b.Symbols().New("predicate");
-
-        hoist.InsertBefore(stmt, b.Decl(b.Let(idx_let, ctx.Clone(idx))));
-        ctx.Replace(idx, b.Expr(idx_let));
-
-        auto* cond = b.LessThanEqual(b.Call<u32>(b.Expr(idx_let)), max);
-        if (obj_pred.IsValid()) {
-            cond = b.And(b.Expr(obj_pred), cond);
-        }
-        hoist.InsertBefore(stmt, b.Decl(b.Let(pred, cond)));
-
-        predicates.Add(expr->Declaration(), pred);
-    }
-
-    /// Applies bounds clamping to the index on an array, vector or matrix.
-    /// @param expr the index accessor expression.
-    void ClampIndexAccessor(const sem::IndexAccessorExpression* expr) {
-        auto* max = DynamicLimitFor(expr);
-        if (!max) {
-            return;  // robustness is not required
-        }
-
-        auto* expr_sem = expr->Unwrap()->As<sem::IndexAccessorExpression>();
-        auto idx = CastToU32(expr_sem->Index());
-        auto* clamped_idx = b.Call(wgsl::BuiltinFn::kMin, idx, max);
-        ctx.Replace(expr->Declaration()->index, clamped_idx);
-    }
-
-    /// Applies predication to the non-texture builtin call, if required.
-    void MaybePredicateNonTextureBuiltin(const sem::Call* call, const sem::BuiltinFn* builtin) {
-        // Gather the predications for the builtin arguments
-        const Expression* predicate = nullptr;
-        for (auto* arg : call->Declaration()->args) {
-            if (auto pred = predicates.Get(arg)) {
-                predicate = And(predicate, b.Expr(*pred));
-            }
-        }
-
-        if (predicate) {
-            if (builtin->Fn() == wgsl::BuiltinFn::kWorkgroupUniformLoad) {
-                // https://www.w3.org/TR/WGSL/#workgroupUniformLoad-builtin:
-                //  "Executes a control barrier synchronization function that affects memory and
-                //   atomic operations in the workgroup address space."
-                // Because the call acts like a control barrier, we need to make sure that we still
-                // trigger a workgroup barrier if the predicate fails.
-                PredicateCall(call, predicate,
-                              b.Block(b.CallStmt(b.Call(wgsl::BuiltinFn::kWorkgroupBarrier))));
-            } else {
-                PredicateCall(call, predicate);
-            }
-        }
-    }
-
-    /// Applies predication to texture builtins, based on whether the coordinates, array index and
-    /// level arguments are all in bounds.
-    void PredicateTextureBuiltin(const sem::Call* call, const sem::BuiltinFn* builtin) {
-        if (!TextureBuiltinNeedsRobustness(builtin->Fn())) {
-            return;
-        }
-
-        auto* expr = call->Declaration();
-        auto* stmt = call->Stmt();
-
-        // Indices of the mandatory texture and coords parameters, and the optional
-        // array and level parameters.
-        auto& signature = builtin->Signature();
-        auto texture_arg_idx = signature.IndexOf(core::ParameterUsage::kTexture);
-        auto coords_arg_idx = signature.IndexOf(core::ParameterUsage::kCoords);
-        auto array_arg_idx = signature.IndexOf(core::ParameterUsage::kArrayIndex);
-        auto level_arg_idx = signature.IndexOf(core::ParameterUsage::kLevel);
-
-        auto* texture_arg = expr->args[static_cast<size_t>(texture_arg_idx)];
-
-        // Build the builtin predicate from the arguments
-        const Expression* predicate = nullptr;
-
-        Symbol level_idx, num_levels;
-        if (level_arg_idx >= 0) {
-            auto* param = builtin->Parameters()[static_cast<size_t>(level_arg_idx)];
-            if (param->Type()->IsIntegerScalar()) {
-                // let level_idx = u32(level-arg);
-                level_idx = b.Symbols().New("level_idx");
-                auto* arg = expr->args[static_cast<size_t>(level_arg_idx)];
-                hoist.InsertBefore(stmt,
-                                   b.Decl(b.Let(level_idx, CastToUnsigned(ctx.Clone(arg), 1u))));
-
-                // let num_levels = textureNumLevels(texture-arg);
-                num_levels = b.Symbols().New("num_levels");
-                hoist.InsertBefore(
-                    stmt, b.Decl(b.Let(num_levels, b.Call(wgsl::BuiltinFn::kTextureNumLevels,
-                                                          ctx.Clone(texture_arg)))));
-
-                // predicate: level_idx < num_levels
-                predicate = And(predicate, b.LessThan(level_idx, num_levels));
-
-                // Replace the level argument with `level_idx`
-                ctx.Replace(arg, b.Expr(level_idx));
-            }
-        }
-
-        Symbol coords;
-        if (coords_arg_idx >= 0) {
-            auto* param = builtin->Parameters()[static_cast<size_t>(coords_arg_idx)];
-            if (param->Type()->IsIntegerScalarOrVector()) {
-                // let coords = u32(coords-arg)
-                coords = b.Symbols().New("coords");
-                auto* arg = expr->args[static_cast<size_t>(coords_arg_idx)];
-                hoist.InsertBefore(stmt,
-                                   b.Decl(b.Let(coords, CastToUnsigned(b.Expr(ctx.Clone(arg)),
-                                                                       WidthOf(param->Type())))));
-
-                // predicate: all(coords < textureDimensions(texture))
-                auto* dimensions =
-                    level_idx.IsValid()
-                        ? b.Call(wgsl::BuiltinFn::kTextureDimensions, ctx.Clone(texture_arg),
-                                 b.Call(wgsl::BuiltinFn::kMin, b.Expr(level_idx),
-                                        b.Sub(num_levels, 1_a)))
-                        : b.Call(wgsl::BuiltinFn::kTextureDimensions, ctx.Clone(texture_arg));
-                predicate =
-                    And(predicate, b.Call(wgsl::BuiltinFn::kAll, b.LessThan(coords, dimensions)));
-
-                // Replace the level argument with `coord`
-                ctx.Replace(arg, b.Expr(coords));
-            }
-        }
-
-        if (array_arg_idx >= 0) {
-            // let array_idx = u32(array-arg)
-            auto* arg = expr->args[static_cast<size_t>(array_arg_idx)];
-            auto* num_layers = b.Call(wgsl::BuiltinFn::kTextureNumLayers, ctx.Clone(texture_arg));
-            auto array_idx = b.Symbols().New("array_idx");
-            hoist.InsertBefore(stmt, b.Decl(b.Let(array_idx, CastToUnsigned(ctx.Clone(arg), 1u))));
-
-            // predicate: array_idx < textureNumLayers(texture)
-            predicate = And(predicate, b.LessThan(array_idx, num_layers));
-
-            // Replace the array index argument with `array_idx`
-            ctx.Replace(arg, b.Expr(array_idx));
-        }
-
-        if (predicate) {
-            PredicateCall(call, predicate);
-        }
-    }
-
-    /// Applies bounds clamping to the coordinates, array index and level arguments of the texture
-    /// builtin.
-    void ClampTextureBuiltin(const sem::Call* call, const sem::BuiltinFn* builtin) {
-        if (!TextureBuiltinNeedsRobustness(builtin->Fn())) {
-            return;
-        }
-
-        auto* expr = call->Declaration();
-        auto* stmt = call->Stmt();
-
-        // Indices of the mandatory texture and coords parameters, and the optional
-        // array and level parameters.
-        auto& signature = builtin->Signature();
-        auto texture_arg_idx = signature.IndexOf(core::ParameterUsage::kTexture);
-        auto coords_arg_idx = signature.IndexOf(core::ParameterUsage::kCoords);
-        auto array_arg_idx = signature.IndexOf(core::ParameterUsage::kArrayIndex);
-        auto level_arg_idx = signature.IndexOf(core::ParameterUsage::kLevel);
-
-        auto* texture_arg = expr->args[static_cast<size_t>(texture_arg_idx)];
-
-        // If the level is provided, then we need to clamp this. As the level is used by
-        // textureDimensions() and the texture[Load|Store]() calls, we need to clamp both usages.
-        Symbol level_idx;
-        if (level_arg_idx >= 0) {
-            const auto* param = builtin->Parameters()[static_cast<size_t>(level_arg_idx)];
-            if (param->Type()->IsIntegerScalar()) {
-                const auto* arg = expr->args[static_cast<size_t>(level_arg_idx)];
-                level_idx = b.Symbols().New("level_idx");
-                const auto* num_levels =
-                    b.Call(wgsl::BuiltinFn::kTextureNumLevels, ctx.Clone(texture_arg));
-                const auto* max = b.Sub(num_levels, 1_a);
-                hoist.InsertBefore(
-                    stmt, b.Decl(b.Let(level_idx, b.Call(wgsl::BuiltinFn::kMin,
-                                                         b.Call<u32>(ctx.Clone(arg)), max))));
-                ctx.Replace(arg, b.Expr(level_idx));
-            }
-        }
-
-        // Clamp the coordinates argument
-        if (coords_arg_idx >= 0) {
-            const auto* param = builtin->Parameters()[static_cast<size_t>(coords_arg_idx)];
-            if (param->Type()->IsIntegerScalarOrVector()) {
-                auto* arg = expr->args[static_cast<size_t>(coords_arg_idx)];
-                const auto width = WidthOf(param->Type());
-                const auto* dimensions =
-                    level_idx.IsValid()
-                        ? b.Call(wgsl::BuiltinFn::kTextureDimensions, ctx.Clone(texture_arg),
-                                 level_idx)
-                        : b.Call(wgsl::BuiltinFn::kTextureDimensions, ctx.Clone(texture_arg));
-
-                // dimensions is u32 or vecN<u32>
-                const auto* unsigned_max = b.Sub(dimensions, ScalarOrVec(b.Expr(1_a), width));
-                if (param->Type()->IsSignedIntegerScalarOrVector()) {
-                    const auto* zero = ScalarOrVec(b.Expr(0_a), width);
-                    const auto* signed_max = CastToSigned(unsigned_max, width);
-                    ctx.Replace(arg,
-                                b.Call(wgsl::BuiltinFn::kClamp, ctx.Clone(arg), zero, signed_max));
-                } else {
-                    ctx.Replace(arg, b.Call(wgsl::BuiltinFn::kMin, ctx.Clone(arg), unsigned_max));
-                }
-            }
-        }
-
-        // Clamp the array_index argument, if provided
-        if (array_arg_idx >= 0) {
-            auto* param = builtin->Parameters()[static_cast<size_t>(array_arg_idx)];
-            auto* arg = expr->args[static_cast<size_t>(array_arg_idx)];
-            auto* num_layers = b.Call(wgsl::BuiltinFn::kTextureNumLayers, ctx.Clone(texture_arg));
-
-            const auto* unsigned_max = b.Sub(num_layers, 1_a);
-            if (param->Type()->IsSignedIntegerScalar()) {
-                const auto* signed_max = CastToSigned(unsigned_max, 1u);
-                ctx.Replace(arg, b.Call(wgsl::BuiltinFn::kClamp, ctx.Clone(arg), 0_a, signed_max));
-            } else {
-                ctx.Replace(arg, b.Call(wgsl::BuiltinFn::kMin, ctx.Clone(arg), unsigned_max));
-            }
-        }
-    }
-
-    /// @param type builtin type
-    /// @returns true if the given builtin is a texture function that requires predication or
-    /// clamping of arguments.
-    bool TextureBuiltinNeedsRobustness(wgsl::BuiltinFn type) {
-        return type == wgsl::BuiltinFn::kTextureLoad || type == wgsl::BuiltinFn::kTextureDimensions;
-    }
-
-    /// @returns a bitwise and of the two expressions, or the other expression if one is null.
-    const Expression* And(const Expression* lhs, const Expression* rhs) {
-        if (lhs && rhs) {
-            return b.And(lhs, rhs);
-        }
-        if (lhs) {
-            return lhs;
-        }
-        return rhs;
-    }
-
-    /// Transforms a call statement or expression so that the expression is predicated by @p
-    /// predicate.
-    /// @param else_stmt - the statement to execute for the predication failure
-    void PredicateCall(const sem::Call* call,
-                       const Expression* predicate,
-                       const BlockStatement* else_stmt = nullptr) {
-        auto* expr = call->Declaration();
-        auto* stmt = call->Stmt();
-        auto* call_stmt = stmt->Declaration()->As<CallStatement>();
-        if (call_stmt && call_stmt->expr == expr) {
-            // Wrap the statement in an if-statement with the predicate condition.
-            hoist.Replace(stmt, b.If(predicate, b.Block(ctx.Clone(stmt->Declaration())),
-                                     ProgramBuilder::ElseStmt(else_stmt)));
-        } else {
-            // Emit the following before the expression's statement:
-            //   var predicated_value : return-type;
-            //   if (predicate) {
-            //     predicated_value = call(...);
-            //   }
-            auto value = b.Symbols().New("predicated_value");
-            hoist.InsertBefore(stmt, b.Decl(b.Var(value, CreateASTTypeFor(ctx, call->Type()))));
-            hoist.InsertBefore(stmt, b.If(predicate, b.Block(b.Assign(value, ctx.Clone(expr))),
-                                          ProgramBuilder::ElseStmt(else_stmt)));
-
-            // Replace the call expression with `predicated_value`
-            ctx.Replace(expr, b.Expr(value));
-        }
-    }
-
-    /// @returns true if @p action is enabled for any address space
-    bool HasAction(Action action) const {
-        return action == cfg.function_action ||   //
-               action == cfg.texture_action ||    //
-               action == cfg.private_action ||    //
-               action == cfg.immediate_action ||  //
-               action == cfg.storage_action ||    //
-               action == cfg.uniform_action ||    //
-               action == cfg.workgroup_action;
-    }
-
-    /// @returns the robustness action to perform for an OOB access with the expression @p expr
-    Action ActionFor(const sem::ValueExpression* expr) {
-        return Switch(
-            expr->Type(),  //
-            [&](const core::type::Reference* t) { return ActionFor(t->AddressSpace()); },
-            [&](Default) { return cfg.value_action; });
-    }
-
-    /// @returns the robustness action to perform for an OOB access in the address space @p
-    /// address_space
-    Action ActionFor(core::AddressSpace address_space) {
-        switch (address_space) {
-            case core::AddressSpace::kFunction:
-                return cfg.function_action;
-            case core::AddressSpace::kHandle:
-                return cfg.texture_action;
-            case core::AddressSpace::kPrivate:
-                return cfg.private_action;
-            case core::AddressSpace::kImmediate:
-                return cfg.immediate_action;
-            case core::AddressSpace::kStorage:
-                return cfg.storage_action;
-            case core::AddressSpace::kUniform:
-                return cfg.uniform_action;
-            case core::AddressSpace::kWorkgroup:
-                return cfg.workgroup_action;
-            default:
-                break;
-        }
-        TINT_UNREACHABLE() << "unhandled address space" << address_space;
-    }
-
-    /// @returns the vector width of @p ty, or 1 if @p ty is not a vector
-    static uint32_t WidthOf(const core::type::Type* ty) {
-        if (auto* vec = ty->As<core::type::Vector>()) {
-            return vec->Width();
-        }
-        return 1u;
-    }
-
-    /// @returns a scalar or vector type with the element type @p scalar and width @p width
-    Type ScalarOrVecTy(Type scalar, uint32_t width) const {
-        if (width > 1) {
-            return b.ty.vec(scalar, width);
-        }
-        return scalar;
-    }
-
-    /// @returns a vector constructed with the scalar expression @p scalar if @p width > 1,
-    /// otherwise returns @p scalar.
-    const Expression* ScalarOrVec(const Expression* scalar, uint32_t width) {
-        if (width > 1) {
-            return b.Call(b.ty.vec<Infer>(width), scalar);
-        }
-        return scalar;
-    }
-
-    /// @returns @p val cast to a `vecN<i32>`, where `N` is @p width, or cast to i32 if @p width
-    /// is 1.
-    const CallExpression* CastToSigned(const Expression* val, uint32_t width) {
-        return b.Call(ScalarOrVecTy(b.ty.i32(), width), val);
-    }
-
-    /// @returns @p val cast to a `vecN<u32>`, where `N` is @p width, or cast to u32 if @p width
-    /// is 1.
-    const CallExpression* CastToUnsigned(const Expression* val, uint32_t width) {
-        return b.Call(ScalarOrVecTy(b.ty.u32(), width), val);
-    }
-
-    /// @returns true if the variable represents a resource binding that should be ignored in the
-    /// robustness check.
-    /// TODO(tint:1890): make this function work with unrestricted pointer paramters. Note that this
-    /// depends on transform::DirectVariableAccess to have been run first.
-    bool IsIgnoredResourceBinding(const sem::Variable* variable) const {
-        auto* globalVariable = tint::As<sem::GlobalVariable>(variable);
-        if (globalVariable == nullptr) {
-            return false;
-        }
-        auto binding_point = globalVariable->Attributes().binding_point;
-        if (!binding_point.has_value()) {
-            return false;
-        }
-        return cfg.bindings_ignored.find(*binding_point) != cfg.bindings_ignored.cend();
-    }
-
-    /// @returns true if expr is an IndexAccessorExpression whose object is a runtime-sized array.
-    bool IsIndexAccessingRuntimeSizedArray(const sem::IndexAccessorExpression* expr) {
-        auto* array_type = expr->Object()->Type()->UnwrapPtrOrRef()->As<core::type::Array>();
-        return array_type != nullptr && array_type->Count()->Is<core::type::RuntimeArrayCount>();
-    }
-
-    /// @returns a clone of expr->Declaration() if it is an unsigned integer scalar, or
-    /// expr->Declaration() cast to u32.
-    const ast::Expression* CastToU32(const sem::ValueExpression* expr) {
-        auto* idx = ctx.Clone(expr->Declaration());
-        if (expr->Type()->IsUnsignedIntegerScalar()) {
-            return idx;
-        }
-        return b.Call<u32>(idx);  // u32(idx)
-    }
-};
-
-Robustness::Config::Config() = default;
-Robustness::Config::Config(const Config&) = default;
-Robustness::Config::~Config() = default;
-Robustness::Config& Robustness::Config::operator=(const Config&) = default;
-
-Robustness::Robustness() = default;
-Robustness::~Robustness() = default;
-
-Transform::ApplyResult Robustness::Apply(const Program& src,
-                                         const DataMap& inputs,
-                                         DataMap&) const {
-    Config cfg;
-    if (auto* cfg_data = inputs.Get<Config>()) {
-        cfg = *cfg_data;
-    }
-
-    return State{src, std::move(cfg)}.Run();
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/robustness.h b/src/tint/lang/wgsl/ast/transform/robustness.h
deleted file mode 100644
index 174e508..0000000
--- a/src/tint/lang/wgsl/ast/transform/robustness.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_WGSL_AST_TRANSFORM_ROBUSTNESS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_ROBUSTNESS_H_
-
-#include <unordered_set>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// This transform is responsible for ensuring that all out of bounds accesses are prevented,
-/// either by conditioning the access (predication) or through clamping of the index to keep the
-/// access in bounds.
-/// @note Robustness must come after:
-///       * PromoteSideEffectsToDecl as Robustness requires side-effecting expressions to be hoisted
-///         to their own statements.
-///       Robustness must come before:
-///       * BuiltinPolyfill as 'clamp' and binary operators may need to be polyfilled.
-///       * CanonicalizeEntryPointIO as the transform does not support the 'in' and 'out' address
-///         spaces.
-class Robustness final : public Castable<Robustness, Transform> {
-  public:
-    /// Robustness action for out-of-bounds indexing.
-    enum class Action {
-        /// Do nothing to prevent the out-of-bounds action.
-        kIgnore,
-        /// Clamp the index to be within bounds.
-        kClamp,
-        /// Do not execute the read or write if the index is out-of-bounds.
-        kPredicate,
-
-        /// The default action
-        kDefault = kClamp,
-    };
-
-    /// Configuration options for the transform
-    struct Config final : public Castable<Config, Data> {
-        /// Constructor
-        Config();
-
-        /// Copy constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// Assignment operator
-        /// @returns this Config
-        Config& operator=(const Config&);
-
-        /// Robustness action for values
-        Action value_action = Action::kDefault;
-
-        /// Robustness action for non-sampling texture operations
-        Action texture_action = Action::kDefault;
-
-        /// Robustness action for variables in the 'function' address space
-        Action function_action = Action::kDefault;
-        /// Robustness action for variables in the 'private' address space
-        Action private_action = Action::kDefault;
-        /// Robustness action for variables in the 'immediate' address space
-        Action immediate_action = Action::kDefault;
-        /// Robustness action for variables in the 'storage' address space
-        Action storage_action = Action::kDefault;
-        /// Robustness action for variables in the 'uniform' address space
-        Action uniform_action = Action::kDefault;
-        /// Robustness action for variables in the 'workgroup' address space
-        Action workgroup_action = Action::kDefault;
-
-        /// Bindings that should always be applied Actions::kIgnore on
-        std::unordered_set<tint::BindingPoint> bindings_ignored;
-
-        /// If we should disable index clamping on runtime-sized arrays in robustness transform
-        bool disable_runtime_sized_array_index_clamping = false;
-    };
-
-    /// Constructor
-    Robustness();
-    /// Destructor
-    ~Robustness() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_ROBUSTNESS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/robustness_test.cc b/src/tint/lang/wgsl/ast/transform/robustness_test.cc
deleted file mode 100644
index 51da92f..0000000
--- a/src/tint/lang/wgsl/ast/transform/robustness_test.cc
+++ /dev/null
@@ -1,5346 +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/transform/robustness.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-
-static std::ostream& operator<<(std::ostream& out, Robustness::Action action) {
-    switch (action) {
-        case Robustness::Action::kIgnore:
-            return out << "ignore";
-        case Robustness::Action::kClamp:
-            return out << "clamp";
-        case Robustness::Action::kPredicate:
-            return out << "predicate";
-    }
-    return out << "unknown";
-}
-
-namespace {
-
-DataMap Config(Robustness::Action action, bool disable_runtime_sized_array_index_clamping = false) {
-    Robustness::Config cfg;
-    cfg.value_action = action;
-    cfg.texture_action = action;
-    cfg.function_action = action;
-    cfg.private_action = action;
-    cfg.immediate_action = action;
-    cfg.storage_action = action;
-    cfg.uniform_action = action;
-    cfg.workgroup_action = action;
-    cfg.disable_runtime_sized_array_index_clamping = disable_runtime_sized_array_index_clamping;
-    DataMap data;
-    data.Add<Robustness::Config>(cfg);
-    return data;
-}
-
-const char* Expect(Robustness::Action action,
-                   const char* expect_ignore,
-                   const char* expect_clamp,
-                   const char* expect_predicate) {
-    switch (action) {
-        case Robustness::Action::kIgnore:
-            return expect_ignore;
-        case Robustness::Action::kClamp:
-            return expect_clamp;
-        case Robustness::Action::kPredicate:
-            return expect_predicate;
-    }
-    return "<invalid action>";
-}
-
-using RobustnessTest = TransformTestWithParam<Robustness::Action>;
-
-////////////////////////////////////////////////////////////////////////////////
-// Constant sized array
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithLiteral) {
-    auto* src = R"(
-fn f() {
-  var b : f32 = array<f32, 3>()[1i];
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithConst) {
-    auto* src = R"(
-const c : u32 = 1u;
-
-fn f() {
-  let b : f32 = array<f32, 3>()[c];
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithLet) {
-    auto* src = R"(
-fn f() {
-  let l : u32 = 1u;
-  let b : f32 = array<f32, 3>()[l];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn f() {
-  let l : u32 = 1u;
-  let b : f32 = array<f32, 3>()[min(l, 2u)];
-}
-)",
-                          /* predicate */ R"(
-fn f() {
-  let l : u32 = 1u;
-  let index = l;
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = array<f32, 3>()[index];
-  }
-  let b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithRuntimeArrayIndex) {
-    auto* src = R"(
-var<private> i : u32;
-
-fn f() {
-  let a = array<f32, 3>();
-  let b = array<i32, 5>();
-  var c : f32 = a[b[i]];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> i : u32;
-
-fn f() {
-  let a = array<f32, 3>();
-  let b = array<i32, 5>();
-  var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> i : u32;
-
-fn f() {
-  let a = array<f32, 3>();
-  let b = array<i32, 5>();
-  let index = i;
-  let predicate = (u32(index) <= 4u);
-  var predicated_expr : i32;
-  if (predicate) {
-    predicated_expr = b[index];
-  }
-  let index_1 = predicated_expr;
-  let predicate_1 = (u32(index_1) <= 2u);
-  var predicated_expr_1 : f32;
-  if (predicate_1) {
-    predicated_expr_1 = a[index_1];
-  }
-  var c : f32 = predicated_expr_1;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithRuntimeExpression) {
-    auto* src = R"(
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = array<f32, 3>()[((c + 2) - 3)];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = array<f32, 3>()[min(u32(((c + 2) - 3)), 2u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> c : i32;
-
-fn f() {
-  let index = ((c + 2) - 3);
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = array<f32, 3>()[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_NestedConstantSizedArraysVal_IndexWithRuntimeExpressions) {
-    auto* src = R"(
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let a = array<array<array<f32, 1>, 2>, 3>();
-  var r = a[x][y][z];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let a = array<array<array<f32, 1>, 2>, 3>();
-  var r = a[min(u32(x), 2u)][min(u32(y), 1u)][min(u32(z), 0u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let a = array<array<array<f32, 1>, 2>, 3>();
-  let index = x;
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : array<array<f32, 1u>, 2u>;
-  if (predicate) {
-    predicated_expr = a[index];
-  }
-  let index_1 = y;
-  let predicate_1 = (u32(index_1) <= 1u);
-  var predicated_expr_1 : array<f32, 1u>;
-  if (predicate_1) {
-    predicated_expr_1 = predicated_expr[index_1];
-  }
-  let index_2 = z;
-  let predicate_2 = (u32(index_2) <= 0u);
-  var predicated_expr_2 : f32;
-  if (predicate_2) {
-    predicated_expr_2 = predicated_expr_1[index_2];
-  }
-  var r = predicated_expr_2;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithOverride) {
-    auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  let a = array<f32, 4>();
-  var b : f32 = a[idx];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  let a = array<f32, 4>();
-  var b : f32 = a[min(u32(idx), 3u)];
-}
-)",
-                          /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  let a = array<f32, 4>();
-  let index = idx;
-  let predicate = (u32(index) <= 3u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithLiteral) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  var b : f32 = a[1i];
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithConst) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-const c : u32 = 1u;
-
-fn f() {
-  let b : f32 = a[c];
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithLet) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let b : f32 = a[l];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let b : f32 = a[min(l, 2u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let index = l;
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[index];
-  }
-  let b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeArrayIndex) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  var c : f32 = a[b[i]];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let index = i;
-  let predicate = (u32(index) <= 4u);
-  var predicated_expr : i32;
-  if (predicate) {
-    predicated_expr = b[index];
-  }
-  let index_1 = predicated_expr;
-  let predicate_1 = (u32(index_1) <= 2u);
-  var predicated_expr_1 : f32;
-  if (predicate_1) {
-    predicated_expr_1 = a[index_1];
-  }
-  var c : f32 = predicated_expr_1;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeArrayIndexViaPointerIndex) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let p1 = &(a);
-  let p2 = &(b);
-  var c : f32 = p1[p2[i]];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let p1 = &(a);
-  let p2 = &(b);
-  var c : f32 = p1[min(u32(p2[min(i, 4u)]), 2u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let p1 = &(a);
-  let p2 = &(b);
-  let index = i;
-  let predicate = (u32(index) <= 4u);
-  var predicated_expr : i32;
-  if (predicate) {
-    predicated_expr = p2[index];
-  }
-  let index_1 = predicated_expr;
-  let predicate_1 = (u32(index_1) <= 2u);
-  var predicated_expr_1 : f32;
-  if (predicate_1) {
-    predicated_expr_1 = p1[index_1];
-  }
-  var c : f32 = predicated_expr_1;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeExpression) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a[((c + 2) - 3)];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> c : i32;
-
-fn f() {
-  let index = ((c + 2) - 3);
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_NestedConstantSizedArraysRef_IndexWithRuntimeExpressions) {
-    auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  var r = a[x][y][z];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  var r = a[min(u32(x), 2u)][min(u32(y), 1u)][min(u32(z), 0u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let index = x;
-  let predicate = (u32(index) <= 2u);
-  let index_1 = y;
-  let predicate_1 = (predicate & (u32(index_1) <= 1u));
-  let index_2 = z;
-  let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
-  var predicated_expr : f32;
-  if (predicate_2) {
-    predicated_expr = a[index][index_1][index_2];
-  }
-  var r = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithOverride) {
-    auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : array<f32, 4>;
-  var b : f32 = a[idx];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : array<f32, 4>;
-  var b : f32 = a[min(u32(idx), 3u)];
-}
-)",
-                          /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : array<f32, 4>;
-  let index = idx;
-  let predicate = (u32(index) <= 3u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayPtr_IndexWithLet) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let p = &(a[l]);
-  let f : f32 = *(p);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let p = &(a[min(l, 2u)]);
-  let f : f32 = *(p);
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let index = l;
-  let predicate = (u32(index) <= 2u);
-  let p = &(a[index]);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = *(p);
-  }
-  let f : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_ConstantSizedArrayPtr_IndexWithRuntimeArrayIndex) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let pa = &(a);
-  let pb = &(b);
-  let p0 = &((*(pb))[i]);
-  let p1 = &(a[*(p0)]);
-  var x : f32 = *(p1);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let pa = &(a);
-  let pb = &(b);
-  let p0 = &((*(pb))[min(i, 4u)]);
-  let p1 = &(a[min(u32(*(p0)), 2u)]);
-  var x : f32 = *(p1);
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let pa = &(a);
-  let pb = &(b);
-  let index = i;
-  let predicate = (u32(index) <= 4u);
-  let p0 = &((*(pb))[index]);
-  var predicated_expr : i32;
-  if (predicate) {
-    predicated_expr = *(p0);
-  }
-  let index_1 = predicated_expr;
-  let predicate_1 = (u32(index_1) <= 2u);
-  let p1 = &(a[index_1]);
-  var predicated_expr_1 : f32;
-  if (predicate_1) {
-    predicated_expr_1 = *(p1);
-  }
-  var x : f32 = predicated_expr_1;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_NestedConstantSizedArraysPtr_IndexWithRuntimeExpressions) {
-    auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let p1 = &((*(p0))[x]);
-  let p2 = &((*(p1))[y]);
-  let p3 = &((*(p2))[z]);
-  var r = *(p3);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let p1 = &((*(p0))[min(u32(x), 2u)]);
-  let p2 = &((*(p1))[min(u32(y), 1u)]);
-  let p3 = &((*(p2))[min(u32(z), 0u)]);
-  var r = *(p3);
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let index = x;
-  let predicate = (u32(index) <= 2u);
-  let p1 = &((*(p0))[index]);
-  let index_1 = y;
-  let predicate_1 = (predicate & (u32(index_1) <= 1u));
-  let p2 = &((*(p1))[index_1]);
-  let index_2 = z;
-  let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
-  let p3 = &((*(p2))[index_2]);
-  var predicated_expr : f32;
-  if (predicate_2) {
-    predicated_expr = *(p3);
-  }
-  var r = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_NestedConstantSizedArrays_MixedAccess) {
-    auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let p1 = &((*(p0))[x]);
-  let p2 = &((*(p1))[y]);
-  let p3 = &((*(p2))[z]);
-  var r = *(p3);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let p1 = &((*(p0))[min(u32(x), 2u)]);
-  let p2 = &((*(p1))[y]);
-  let p3 = &((*(p2))[min(u32(z), 0u)]);
-  var r = *(p3);
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let index = x;
-  let predicate = (u32(index) <= 2u);
-  let p1 = &((*(p0))[index]);
-  let p2 = &((*(p1))[y]);
-  let index_1 = z;
-  let predicate_1 = (predicate & (u32(index_1) <= 0u));
-  let p3 = &((*(p2))[index_1]);
-  var predicated_expr : f32;
-  if (predicate_1) {
-    predicated_expr = *(p3);
-  }
-  var r = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_ConstantSizedArray_IndexWithLet) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  a[l] = 42.0f;
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  a[min(l, 2u)] = 42.0f;
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let index = l;
-  let predicate = (u32(index) <= 2u);
-  if (predicate) {
-    a[index] = 42.0f;
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_ConstantSizedArrayPtr_IndexWithLet) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let p = &(a[l]);
-  *(p) = 42.0f;
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let p = &(a[min(l, 2u)]);
-  *(p) = 42.0f;
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let index = l;
-  let predicate = (u32(index) <= 2u);
-  let p = &(a[index]);
-  if (predicate) {
-    *(p) = 42.0f;
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_ConstantSizedArrayPtr_IndexWithRuntimeArrayIndex) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let pa = &(a);
-  let pb = &(b);
-  let p0 = &((*(pb))[i]);
-  let p1 = &(a[*(p0)]);
-  *(p1) = 42.0f;
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let pa = &(a);
-  let pb = &(b);
-  let p0 = &((*(pb))[min(i, 4u)]);
-  let p1 = &(a[min(u32(*(p0)), 2u)]);
-  *(p1) = 42.0f;
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-var<private> b : array<i32, 5>;
-
-var<private> i : u32;
-
-fn f() {
-  let pa = &(a);
-  let pb = &(b);
-  let index = i;
-  let predicate = (u32(index) <= 4u);
-  let p0 = &((*(pb))[index]);
-  var predicated_expr : i32;
-  if (predicate) {
-    predicated_expr = *(p0);
-  }
-  let index_1 = predicated_expr;
-  let predicate_1 = (u32(index_1) <= 2u);
-  let p1 = &(a[index_1]);
-  if (predicate_1) {
-    *(p1) = 42.0f;
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_NestedConstantSizedArraysPtr_IndexWithRuntimeExpressions) {
-    auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let p1 = &((*(p0))[x]);
-  let p2 = &((*(p1))[y]);
-  let p3 = &((*(p2))[z]);
-  *(p3) = 42.0f;
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let p1 = &((*(p0))[min(u32(x), 2u)]);
-  let p2 = &((*(p1))[min(u32(y), 1u)]);
-  let p3 = &((*(p2))[min(u32(z), 0u)]);
-  *(p3) = 42.0f;
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-var<private> y : i32;
-
-var<private> z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let index = x;
-  let predicate = (u32(index) <= 2u);
-  let p1 = &((*(p0))[index]);
-  let index_1 = y;
-  let predicate_1 = (predicate & (u32(index_1) <= 1u));
-  let p2 = &((*(p1))[index_1]);
-  let index_2 = z;
-  let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
-  let p3 = &((*(p2))[index_2]);
-  if (predicate_2) {
-    *(p3) = 42.0f;
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_NestedConstantSizedArrays_MixedAccess) {
-    auto* src = R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let p1 = &((*(p0))[x]);
-  let p2 = &((*(p1))[y]);
-  let p3 = &((*(p2))[z]);
-  *(p3) = 42.0f;
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let p1 = &((*(p0))[min(u32(x), 2u)]);
-  let p2 = &((*(p1))[y]);
-  let p3 = &((*(p2))[min(u32(z), 0u)]);
-  *(p3) = 42.0f;
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<array<array<f32, 1>, 2>, 3>;
-
-var<private> x : i32;
-
-const y = 1;
-
-override z : i32;
-
-fn f() {
-  let p0 = &(a);
-  let index = x;
-  let predicate = (u32(index) <= 2u);
-  let p1 = &((*(p0))[index]);
-  let p2 = &((*(p1))[y]);
-  let index_1 = z;
-  let predicate_1 = (predicate & (u32(index_1) <= 0u));
-  let p3 = &((*(p2))[index_1]);
-  if (predicate_1) {
-    *(p3) = 42.0f;
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, CompoundAssign_ConstantSizedArray_IndexWithLet) {
-    auto* src = R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  a[l] += 42.0f;
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  a[min(l, 2u)] += 42.0f;
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<f32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let index = l;
-  let predicate = (u32(index) <= 2u);
-  if (predicate) {
-    a[index] += 42.0f;
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Increment_ConstantSizedArray_IndexWithLet) {
-    auto* src = R"(
-var<private> a : array<i32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  a[l]++;
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<i32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  a[min(l, 2u)]++;
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<i32, 3>;
-
-fn f() {
-  let l : u32 = 1u;
-  let index = l;
-  let predicate = (u32(index) <= 2u);
-  if (predicate) {
-    a[index]++;
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Runtime sized array
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_RuntimeArray_IndexWithLiteral) {
-    auto* src = R"(
-struct S {
-  a : f32,
-  b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-fn f() {
-  var d : f32 = s.b[25];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-struct S {
-  a : f32,
-  b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-fn f() {
-  var d : f32 = s.b[min(u32(25), (arrayLength(&(s.b)) - 1u))];
-}
-)",
-                          /* predicate */ R"(
-struct S {
-  a : f32,
-  b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-fn f() {
-  let index = 25;
-  let predicate = (u32(index) <= (arrayLength(&(s.b)) - 1u));
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = s.b[index];
-  }
-  var d : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Vector
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithLiteral) {
-    auto* src = R"(
-var<private> a : vec3<f32>;
-
-fn f() {
-  var b : f32 = a[1i];
-}
-)";
-
-    auto* expect = src;
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithConst) {
-    auto* src = R"(
-var<private> a : vec3<f32>;
-
-fn f() {
-  const i = 1;
-  var b : f32 = a[i];
-}
-)";
-
-    auto* expect = src;
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithLet) {
-    auto* src = R"(
-fn f() {
-  let i = 99;
-  let v = vec4<f32>()[i];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn f() {
-  let i = 99;
-  let v = vec4<f32>()[min(u32(i), 3u)];
-}
-)",
-                          /* predicate */ R"(
-fn f() {
-  let i = 99;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = vec4<f32>()[index];
-  }
-  let v = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithRuntimeExpression) {
-    auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a[((c + 2) - 3)];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let index = ((c + 2) - 3);
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithRuntimeExpression_ViaPointerIndex) {
-    auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let p = &(a);
-  var b : f32 = p[((c + 2) - 3)];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let p = &(a);
-  var b : f32 = p[min(u32(((c + 2) - 3)), 2u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let p = &(a);
-  let index = ((c + 2) - 3);
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = p[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithGlobalVar) {
-    auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a.xy[c];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a.xy[min(u32(c), 1u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let index = c;
-  let predicate = (u32(index) <= 1u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a.xy[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithRuntimeExpression) {
-    auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a.xy[((c + 2) - 3)];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let index = ((c + 2) - 3);
-  let predicate = (u32(index) <= 1u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a.xy[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithRuntimeExpression_ViaPointerDot) {
-    auto* src = R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let p = &(a);
-  var b : f32 = p.xy[((c + 2) - 3)];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let p = &(a);
-  var b : f32 = p.xy[min(u32(((c + 2) - 3)), 1u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : vec3<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let p = &(a);
-  let index = ((c + 2) - 3);
-  let predicate = (u32(index) <= 1u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = p.xy[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_Vector_IndexWithOverride) {
-    auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : vec3<f32>;
-  var b : f32 = a[idx];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : vec3<f32>;
-  var b : f32 = a[min(u32(idx), 2u)];
-}
-)",
-                          /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : vec3<f32>;
-  let index = idx;
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Matrix
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexingWithLiterals) {
-    auto* src = R"(
-var<private> a : mat3x2<f32>;
-
-fn f() {
-  var b : f32 = a[2i][1i];
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithRuntimeExpressionThenLiteral) {
-    auto* src = R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a[((c + 2) - 3)][1];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let index = ((c + 2) - 3);
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[index][1];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLiteralThenRuntimeExpression) {
-    auto* src = R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a[1][((c + 2) - 3)];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)];
-}
-)",
-                          /* predicate */ R"(
-var<private> a : mat3x2<f32>;
-
-var<private> c : i32;
-
-fn f() {
-  let index = ((c + 2) - 3);
-  let predicate = (u32(index) <= 1u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[1][index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithOverrideThenLiteral) {
-    auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : mat3x2<f32>;
-  var b : f32 = a[idx][1];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : mat3x2<f32>;
-  var b : f32 = a[min(u32(idx), 2u)][1];
-}
-)",
-                          /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : mat3x2<f32>;
-  let index = idx;
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[index][1];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLetThenSwizzle) {
-    auto* src = R"(
-fn f() {
-  let i = 1;
-  var m = mat3x2<f32>();
-  var v = m[i].yx;
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn f() {
-  let i = 1;
-  var m = mat3x2<f32>();
-  var v = m[min(u32(i), 2u)].yx;
-}
-)",
-                          /* predicate */ R"(
-fn f() {
-  let i = 1;
-  var m = mat3x2<f32>();
-  let index = i;
-  let predicate = (u32(index) <= 2u);
-  var predicated_expr : vec2<f32>;
-  if (predicate) {
-    predicated_expr = m[index];
-  }
-  var v = predicated_expr.yx;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLiteralThenOverride) {
-    auto* src = R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : mat3x2<f32>;
-  var b : f32 = a[1][idx];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : mat3x2<f32>;
-  var b : f32 = a[1][min(u32(idx), 1u)];
-}
-)",
-                          /* predicate */ R"(
-@id(1300) override idx : i32;
-
-fn f() {
-  var a : mat3x2<f32>;
-  let index = idx;
-  let predicate = (u32(index) <= 1u);
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = a[1][index];
-  }
-  var b : f32 = predicated_expr;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_Matrix_IndexWithLet) {
-    auto* src = R"(
-var<private> m : mat3x4f;
-
-fn f() {
-  let c = 1;
-  m[c] = vec4f(1);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> m : mat3x4f;
-
-fn f() {
-  let c = 1;
-  m[min(u32(c), 2u)] = vec4f(1);
-}
-)",
-                          /* predicate */ R"(
-var<private> m : mat3x4f;
-
-fn f() {
-  let c = 1;
-  let index = c;
-  let predicate = (u32(index) <= 2u);
-  if (predicate) {
-    m[index] = vec4f(1);
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, CompoundAssign_Matrix_IndexWithLet) {
-    auto* src = R"(
-var<private> m : mat3x4f;
-
-fn f() {
-  let c = 1;
-  m[c] += vec4f(1);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> m : mat3x4f;
-
-fn f() {
-  let c = 1;
-  m[min(u32(c), 2u)] += vec4f(1);
-}
-)",
-                          /* predicate */ R"(
-var<private> m : mat3x4f;
-
-fn f() {
-  let c = 1;
-  let index = c;
-  let predicate = (u32(index) <= 2u);
-  if (predicate) {
-    m[index] += vec4f(1);
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Texture builtin calls.
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, TextureDimensions) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn dimensions() {
-  let l = textureDimensions(t);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureDimensions_Level) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn dimensions_signed(level : i32) {
-  let l = textureDimensions(t, level);
-}
-
-fn dimensions_unsigned(level : u32) {
-  let l = textureDimensions(t, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  dimensions_signed(i32(non_uniform.x));
-  dimensions_unsigned(u32(non_uniform.x));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn dimensions_signed(level : i32) {
-  let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureDimensions(t, level_idx);
-}
-
-fn dimensions_unsigned(level : u32) {
-  let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureDimensions(t, level_idx_1);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  dimensions_signed(i32(non_uniform.x));
-  dimensions_unsigned(u32(non_uniform.x));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn dimensions_signed(level : i32) {
-  let level_idx = u32(level);
-  let num_levels = textureNumLevels(t);
-  var predicated_value : vec2<u32>;
-  if ((level_idx < num_levels)) {
-    predicated_value = textureDimensions(t, level_idx);
-  }
-  let l = predicated_value;
-}
-
-fn dimensions_unsigned(level : u32) {
-  let level_idx_1 = u32(level);
-  let num_levels_1 = textureNumLevels(t);
-  var predicated_value_1 : vec2<u32>;
-  if ((level_idx_1 < num_levels_1)) {
-    predicated_value_1 = textureDimensions(t, level_idx_1);
-  }
-  let l = predicated_value_1;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  dimensions_signed(i32(non_uniform.x));
-  dimensions_unsigned(u32(non_uniform.x));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureGather) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn gather(coords : vec2f) {
-  let l = textureGather(0, t, s, coords);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureGather_Array) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn gather_signed(coords : vec2f, array : i32) {
-  let l = textureGather(1, t, s, coords, array);
-}
-
-fn gather_unsigned(coords : vec2f, array : u32) {
-  let l = textureGather(1, t, s, coords, array);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  gather_signed(non_uniform.xy, i32(non_uniform.x));
-  gather_unsigned(non_uniform.xy, u32(non_uniform.x));
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureGatherCompare) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn gather(coords : vec2f, depth_ref : f32) {
-  let l = textureGatherCompare(t, s, coords, depth_ref);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureGatherCompare_Array) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn gather_signed(coords : vec2f, array : i32, depth_ref : f32) {
-  let l = textureGatherCompare(t, s, coords, array, depth_ref);
-}
-
-fn gather_unsigned(coords : vec2f, array : u32, depth_ref : f32) {
-  let l = textureGatherCompare(t, s, coords, array, depth_ref);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  gather_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
-  gather_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_1D) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_1d<f32>;
-
-fn load_signed(coords : i32, level : i32) {
-  let l = textureLoad(t, coords, level);
-}
-
-fn load_unsigned(coords : u32, level : u32) {
-  let l = textureLoad(t, coords, level);
-}
-
-fn load_mixed(coords : i32, level : u32) {
-  let l = textureLoad(t, coords, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(i32(non_uniform.x), u32(non_uniform.x));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_1d<f32>;
-
-fn load_signed(coords : i32, level : i32) {
-  let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, clamp(coords, 0, i32((textureDimensions(t, level_idx) - 1))), level_idx);
-}
-
-fn load_unsigned(coords : u32, level : u32) {
-  let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - 1)), level_idx_1);
-}
-
-fn load_mixed(coords : i32, level : u32) {
-  let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, clamp(coords, 0, i32((textureDimensions(t, level_idx_2) - 1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(i32(non_uniform.x), u32(non_uniform.x));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_1d<f32>;
-
-fn load_signed(coords : i32, level : i32) {
-  let level_idx = u32(level);
-  let num_levels = textureNumLevels(t);
-  let coords_1 = u32(coords);
-  var predicated_value : vec4<f32>;
-  if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
-    predicated_value = textureLoad(t, coords_1, level_idx);
-  }
-  let l = predicated_value;
-}
-
-fn load_unsigned(coords : u32, level : u32) {
-  let level_idx_1 = u32(level);
-  let num_levels_1 = textureNumLevels(t);
-  let coords_2 = u32(coords);
-  var predicated_value_1 : vec4<f32>;
-  if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
-    predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
-  }
-  let l = predicated_value_1;
-}
-
-fn load_mixed(coords : i32, level : u32) {
-  let level_idx_2 = u32(level);
-  let num_levels_2 = textureNumLevels(t);
-  let coords_3 = u32(coords);
-  var predicated_value_2 : vec4<f32>;
-  if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
-    predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
-  }
-  let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(i32(non_uniform.x), u32(non_uniform.x));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_2D) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn load_signed(coords : vec2i, level : i32) {
-  let l = textureLoad(t, coords, level);
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
-  let l = textureLoad(t, coords, level);
-}
-
-fn load_mixed(coords : vec2u, level : i32) {
-  let l = textureLoad(t, coords, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn load_signed(coords : vec2i, level : i32) {
-  let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx);
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
-  let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), level_idx_1);
-}
-
-fn load_mixed(coords : vec2u, level : i32) {
-  let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-fn load_signed(coords : vec2i, level : i32) {
-  let level_idx = u32(level);
-  let num_levels = textureNumLevels(t);
-  let coords_1 = vec2<u32>(coords);
-  var predicated_value : vec4<f32>;
-  if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
-    predicated_value = textureLoad(t, coords_1, level_idx);
-  }
-  let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
-  let level_idx_1 = u32(level);
-  let num_levels_1 = textureNumLevels(t);
-  let coords_2 = vec2<u32>(coords);
-  var predicated_value_1 : vec4<f32>;
-  if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
-    predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
-  }
-  let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2u, level : i32) {
-  let level_idx_2 = u32(level);
-  let num_levels_2 = textureNumLevels(t);
-  let coords_3 = vec2<u32>(coords);
-  var predicated_value_2 : vec4<f32>;
-  if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
-    predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
-  }
-  let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_2DArray) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
-  let l = textureLoad(t, coords, array, level);
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
-  let l = textureLoad(t, coords, array, level);
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
-  let l = textureLoad(t, coords, array, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
-  let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx);
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
-  let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), min(array, (textureNumLayers(t) - 1)), level_idx_1);
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
-  let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
-  let level_idx = u32(level);
-  let num_levels = textureNumLevels(t);
-  let coords_1 = vec2<u32>(coords);
-  let array_idx = u32(array);
-  var predicated_value : vec4<f32>;
-  if ((((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1)))))) & (array_idx < textureNumLayers(t)))) {
-    predicated_value = textureLoad(t, coords_1, array_idx, level_idx);
-  }
-  let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
-  let level_idx_1 = u32(level);
-  let num_levels_1 = textureNumLevels(t);
-  let coords_2 = vec2<u32>(coords);
-  let array_idx_1 = u32(array);
-  var predicated_value_1 : vec4<f32>;
-  if ((((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1)))))) & (array_idx_1 < textureNumLayers(t)))) {
-    predicated_value_1 = textureLoad(t, coords_2, array_idx_1, level_idx_1);
-  }
-  let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
-  let level_idx_2 = u32(level);
-  let num_levels_2 = textureNumLevels(t);
-  let coords_3 = vec2<u32>(coords);
-  let array_idx_2 = u32(array);
-  var predicated_value_2 : vec4<f32>;
-  if ((((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1)))))) & (array_idx_2 < textureNumLayers(t)))) {
-    predicated_value_2 = textureLoad(t, coords_3, array_idx_2, level_idx_2);
-  }
-  let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_3D) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_3d<f32>;
-
-fn load_signed(coords : vec3i, level : i32) {
-  let l = textureLoad(t, coords, level);
-}
-
-fn load_unsigned(coords : vec3u, level : u32) {
-  let l = textureLoad(t, coords, level);
-}
-
-fn load_mixed(coords : vec3u, level : i32) {
-  let l = textureLoad(t, coords, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
-  load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
-  load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_3d<f32>;
-
-fn load_signed(coords : vec3i, level : i32) {
-  let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, clamp(coords, vec3(0), vec3<i32>((textureDimensions(t, level_idx) - vec3(1)))), level_idx);
-}
-
-fn load_unsigned(coords : vec3u, level : u32) {
-  let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec3(1))), level_idx_1);
-}
-
-fn load_mixed(coords : vec3u, level : i32) {
-  let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec3(1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
-  load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
-  load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_3d<f32>;
-
-fn load_signed(coords : vec3i, level : i32) {
-  let level_idx = u32(level);
-  let num_levels = textureNumLevels(t);
-  let coords_1 = vec3<u32>(coords);
-  var predicated_value : vec4<f32>;
-  if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
-    predicated_value = textureLoad(t, coords_1, level_idx);
-  }
-  let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec3u, level : u32) {
-  let level_idx_1 = u32(level);
-  let num_levels_1 = textureNumLevels(t);
-  let coords_2 = vec3<u32>(coords);
-  var predicated_value_1 : vec4<f32>;
-  if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
-    predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
-  }
-  let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec3u, level : i32) {
-  let level_idx_2 = u32(level);
-  let num_levels_2 = textureNumLevels(t);
-  let coords_3 = vec3<u32>(coords);
-  var predicated_value_2 : vec4<f32>;
-  if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
-    predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
-  }
-  let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
-  load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
-  load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_Multisampled2D) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
-
-fn load_signed(coords : vec2i, sample : i32) {
-  let l = textureLoad(t, coords, sample);
-}
-
-fn load_unsigned(coords : vec2u, sample : u32) {
-  let l = textureLoad(t, coords, sample);
-}
-
-fn load_mixed(coords : vec2i, sample : u32) {
-  let l = textureLoad(t, coords, sample);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
-
-fn load_signed(coords : vec2i, sample : i32) {
-  let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))), sample);
-}
-
-fn load_unsigned(coords : vec2u, sample : u32) {
-  let l = textureLoad(t, min(coords, (textureDimensions(t) - vec2(1))), sample);
-}
-
-fn load_mixed(coords : vec2i, sample : u32) {
-  let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))), sample);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
-
-fn load_signed(coords : vec2i, sample : i32) {
-  let coords_1 = vec2<u32>(coords);
-  var predicated_value : vec4<f32>;
-  if (all((coords_1 < textureDimensions(t)))) {
-    predicated_value = textureLoad(t, coords_1, sample);
-  }
-  let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, sample : u32) {
-  let coords_2 = vec2<u32>(coords);
-  var predicated_value_1 : vec4<f32>;
-  if (all((coords_2 < textureDimensions(t)))) {
-    predicated_value_1 = textureLoad(t, coords_2, sample);
-  }
-  let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2i, sample : u32) {
-  let coords_3 = vec2<u32>(coords);
-  var predicated_value_2 : vec4<f32>;
-  if (all((coords_3 < textureDimensions(t)))) {
-    predicated_value_2 = textureLoad(t, coords_3, sample);
-  }
-  let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_Depth2D) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-fn load_signed(coords : vec2i, level : i32) {
-  let l = textureLoad(t, coords, level);
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
-  let l = textureLoad(t, coords, level);
-}
-
-fn load_mixed(coords : vec2i, level : u32) {
-  let l = textureLoad(t, coords, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-fn load_signed(coords : vec2i, level : i32) {
-  let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx);
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
-  let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), level_idx_1);
-}
-
-fn load_mixed(coords : vec2i, level : u32) {
-  let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx_2) - vec2(1)))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-fn load_signed(coords : vec2i, level : i32) {
-  let level_idx = u32(level);
-  let num_levels = textureNumLevels(t);
-  let coords_1 = vec2<u32>(coords);
-  var predicated_value : f32;
-  if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
-    predicated_value = textureLoad(t, coords_1, level_idx);
-  }
-  let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, level : u32) {
-  let level_idx_1 = u32(level);
-  let num_levels_1 = textureNumLevels(t);
-  let coords_2 = vec2<u32>(coords);
-  var predicated_value_1 : f32;
-  if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
-    predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
-  }
-  let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2i, level : u32) {
-  let level_idx_2 = u32(level);
-  let num_levels_2 = textureNumLevels(t);
-  let coords_3 = vec2<u32>(coords);
-  var predicated_value_2 : f32;
-  if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
-    predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
-  }
-  let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
-  load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_Depth2DArray) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
-  let l = textureLoad(t, coords, array, level);
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
-  let l = textureLoad(t, coords, array, level);
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
-  let l = textureLoad(t, coords, array, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
-  let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx);
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
-  let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), min(array, (textureNumLayers(t) - 1)), level_idx_1);
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
-  let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
-  let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx_2);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-fn load_signed(coords : vec2i, array : i32, level : i32) {
-  let level_idx = u32(level);
-  let num_levels = textureNumLevels(t);
-  let coords_1 = vec2<u32>(coords);
-  let array_idx = u32(array);
-  var predicated_value : f32;
-  if ((((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1)))))) & (array_idx < textureNumLayers(t)))) {
-    predicated_value = textureLoad(t, coords_1, array_idx, level_idx);
-  }
-  let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u, array : u32, level : u32) {
-  let level_idx_1 = u32(level);
-  let num_levels_1 = textureNumLevels(t);
-  let coords_2 = vec2<u32>(coords);
-  let array_idx_1 = u32(array);
-  var predicated_value_1 : f32;
-  if ((((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1)))))) & (array_idx_1 < textureNumLayers(t)))) {
-    predicated_value_1 = textureLoad(t, coords_2, array_idx_1, level_idx_1);
-  }
-  let l = predicated_value_1;
-}
-
-fn load_mixed(coords : vec2u, array : i32, level : u32) {
-  let level_idx_2 = u32(level);
-  let num_levels_2 = textureNumLevels(t);
-  let coords_3 = vec2<u32>(coords);
-  let array_idx_2 = u32(array);
-  var predicated_value_2 : f32;
-  if ((((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1)))))) & (array_idx_2 < textureNumLayers(t)))) {
-    predicated_value_2 = textureLoad(t, coords_3, array_idx_2, level_idx_2);
-  }
-  let l = predicated_value_2;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
-  load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
-  load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_External) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_external;
-
-fn load_signed(coords : vec2i) {
-  let l = textureLoad(t, coords);
-}
-
-fn load_unsigned(coords : vec2u) {
-  let l = textureLoad(t, coords);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy));
-  load_unsigned(vec2u(non_uniform.xy));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_external;
-
-fn load_signed(coords : vec2i) {
-  let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))));
-}
-
-fn load_unsigned(coords : vec2u) {
-  let l = textureLoad(t, min(coords, (textureDimensions(t) - vec2(1))));
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy));
-  load_unsigned(vec2u(non_uniform.xy));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_external;
-
-fn load_signed(coords : vec2i) {
-  let coords_1 = vec2<u32>(coords);
-  var predicated_value : vec4<f32>;
-  if (all((coords_1 < textureDimensions(t)))) {
-    predicated_value = textureLoad(t, coords_1);
-  }
-  let l = predicated_value;
-}
-
-fn load_unsigned(coords : vec2u) {
-  let coords_2 = vec2<u32>(coords);
-  var predicated_value_1 : vec4<f32>;
-  if (all((coords_2 < textureDimensions(t)))) {
-    predicated_value_1 = textureLoad(t, coords_2);
-  }
-  let l = predicated_value_1;
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  load_signed(vec2i(non_uniform.xy));
-  load_unsigned(vec2u(non_uniform.xy));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureNumLayers) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-fn num_layers(coords : vec2f, depth_ref : f32) {
-  let l = textureNumLayers(t);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureNumLevels) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-fn num_levels(coords : vec2f, depth_ref : f32) {
-  let l = textureNumLevels(t);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureNumSamples) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_multisampled_2d;
-
-fn num_levels(coords : vec2f, depth_ref : f32) {
-  let l = textureNumSamples(t);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSample) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample(coords : vec2f) {
-  let l = textureSample(t, s, coords);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSample_Offset) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample(coords : vec2f) {
-  const offset = vec2i(1);
-  let l = textureSample(t, s, coords, offset);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSample_ArrayIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_signed(coords : vec2f, array : i32) {
-  let l = textureSample(t, s, coords, array);
-}
-
-fn sample_unsigned(coords : vec2f, array : u32) {
-  let l = textureSample(t, s, coords, array);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  sample_signed(non_uniform.xy, i32(non_uniform.x));
-  sample_unsigned(non_uniform.xy, u32(non_uniform.x));
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleBias) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_bias(coords : vec2f, bias : f32) {
-  let l = textureSampleBias(t, s, coords, bias);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleBias_Offset) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_bias(coords : vec2f, bias : f32) {
-  const offset = vec2i(1);
-  let l = textureSampleBias(t, s, coords, bias, offset);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleBias_ArrayIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_bias_signed(coords : vec2f, array : i32, bias : f32) {
-  let l = textureSampleBias(t, s, coords, array, bias);
-}
-
-fn sample_bias_unsigned(coords : vec2f, array : u32, bias : f32) {
-  let l = textureSampleBias(t, s, coords, array, bias);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  sample_bias_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
-  sample_bias_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompare) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare(coords : vec2f, depth_ref : f32) {
-  let l = textureSampleCompare(t, s, coords, depth_ref);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompare_Offset) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare(coords : vec2f, depth_ref : f32) {
-  const offset = vec2i(1);
-  let l = textureSampleCompare(t, s, coords, depth_ref, offset);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompare_ArrayIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare_signed(coords : vec2f, array : i32, depth_ref : f32) {
-  let l = textureSampleCompare(t, s, coords, array, depth_ref);
-}
-
-fn sample_compare_unsigned(coords : vec2f, array : u32, depth_ref : f32) {
-  let l = textureSampleCompare(t, s, coords, array, depth_ref);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  sample_compare_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
-  sample_compare_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompareLevel) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare_level(coords : vec2f, depth_ref : f32) {
-  let l = textureSampleCompareLevel(t, s, coords, depth_ref);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompareLevel_Offset) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare_level(coords : vec2f, depth_ref : f32) {
-  const offset = vec2i(1);
-  let l = textureSampleCompareLevel(t, s, coords, depth_ref, offset);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleCompareLevel_ArrayIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_depth_2d_array;
-
-@group(0) @binding(1) var s : sampler_comparison;
-
-fn sample_compare_level_signed(coords : vec2f, array : i32, depth_ref : f32) {
-  let l = textureSampleCompareLevel(t, s, coords, array, depth_ref);
-}
-
-fn sample_compare_level_unsigned(coords : vec2f, array : u32, depth_ref : f32) {
-  let l = textureSampleCompareLevel(t, s, coords, array, depth_ref);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  sample_compare_level_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
-  sample_compare_level_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleGrad) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level(coords : vec2f, ddx : vec2f, ddy : vec2f) {
-  let l = textureSampleGrad(t, s, coords, ddx, ddy);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleGrad_Offset) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level(coords : vec2f, ddx : vec2f, ddy : vec2f) {
-  const offset = vec2i(1);
-  let l = textureSampleGrad(t, s, coords, ddx, ddy, offset);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleGrad_ArrayIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_grad_signed(coords : vec2f, array : i32, ddx : vec2f, ddy : vec2f) {
-  let l = textureSampleGrad(t, s, coords, array, ddx, ddy);
-}
-
-fn sample_grad_unsigned(coords : vec2f, array : u32, ddx : vec2f, ddy : vec2f) {
-  let l = textureSampleGrad(t, s, coords, array, ddx, ddy);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  sample_grad_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.xy, non_uniform.xy);
-  sample_grad_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.xy, non_uniform.xy);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleLevel) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level(coords : vec2f, level : f32) {
-  let l = textureSampleLevel(t, s, coords, level);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleLevel_Offset) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level(coords : vec2f, level : f32) {
-  const offset = vec2i(1);
-  let l = textureSampleLevel(t, s, coords, level, offset);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleLevel_ArrayIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d_array<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_compare_level_signed(coords : vec2f, array : i32, level : f32) {
-  let l = textureSampleLevel(t, s, coords, array, level);
-}
-
-fn sample_compare_level_unsigned(coords : vec2f, array : u32, level : f32) {
-  let l = textureSampleLevel(t, s, coords, array, level);
-}
-
-@fragment
-fn main(@builtin(position) non_uniform : vec4f) {
-  sample_compare_level_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
-  sample_compare_level_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureSampleBaseClampToEdge) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_external;
-
-@group(0) @binding(1) var s : sampler;
-
-fn sample_base_clamp_to_edge(coords : vec2f) {
-  let l = textureSampleBaseClampToEdge(t, s, coords);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Other
-////////////////////////////////////////////////////////////////////////////////
-TEST_P(RobustnessTest, ShadowedVariable) {
-    auto* src = R"(
-fn f() {
-  var a : array<f32, 3>;
-  var i : u32;
-  {
-    var a : array<f32, 5>;
-    var b : f32 = a[i];
-  }
-  var c : f32 = a[i];
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn f() {
-  var a : array<f32, 3>;
-  var i : u32;
-  {
-    var a : array<f32, 5>;
-    var b : f32 = a[min(i, 4u)];
-  }
-  var c : f32 = a[min(i, 2u)];
-}
-)",
-                          /* predicate */ R"(
-fn f() {
-  var a : array<f32, 3>;
-  var i : u32;
-  {
-    var a : array<f32, 5>;
-    let index = i;
-    let predicate = (u32(index) <= 4u);
-    var predicated_expr : f32;
-    if (predicate) {
-      predicated_expr = a[index];
-    }
-    var b : f32 = predicated_expr;
-  }
-  let index_1 = i;
-  let predicate_1 = (u32(index_1) <= 2u);
-  var predicated_expr_1 : f32;
-  if (predicate_1) {
-    predicated_expr_1 = a[index_1];
-  }
-  var c : f32 = predicated_expr_1;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-    EXPECT_EQ(expect, str(got));
-}
-
-// Check that existing use of min() and arrayLength() do not get renamed.
-TEST_P(RobustnessTest, DontRenameSymbols) {
-    auto* src = R"(
-struct S {
-  a : f32,
-  b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-const c : u32 = 1u;
-
-fn f() {
-  let b : f32 = s.b[c];
-  let x : i32 = min(1, 2);
-  let y : u32 = arrayLength(&(s.b));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-struct S {
-  a : f32,
-  b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-const c : u32 = 1u;
-
-fn f() {
-  let b : f32 = s.b[min(c, (arrayLength(&(s.b)) - 1u))];
-  let x : i32 = min(1, 2);
-  let y : u32 = arrayLength(&(s.b));
-}
-)",
-                          /* predicate */ R"(
-struct S {
-  a : f32,
-  b : array<f32>,
-}
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-const c : u32 = 1u;
-
-fn f() {
-  let index = c;
-  let predicate = (u32(index) <= (arrayLength(&(s.b)) - 1u));
-  var predicated_expr : f32;
-  if (predicate) {
-    predicated_expr = s.b[index];
-  }
-  let b : f32 = predicated_expr;
-  let x : i32 = min(1, 2);
-  let y : u32 = arrayLength(&(s.b));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, WorkgroupOverrideCount) {
-    auto* src = R"(
-override N = 123;
-
-var<workgroup> w : array<f32, N>;
-
-fn f() {
-  var b : f32 = w[1i];
-}
-)";
-
-    auto* expect =
-        Expect(GetParam(),
-               /* ignore */ src,
-               /* clamp */
-               R"(error: array size is an override-expression, when expected a constant-expression.
-Was the SubstituteOverride transform run?)",
-               /* predicate */
-               R"(error: array size is an override-expression, when expected a constant-expression.
-Was the SubstituteOverride transform run?)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// atomic predication
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, AtomicLoad) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  let i = 0;
-  let r = atomicLoad(&(a[i]));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  let i = 0;
-  let r = atomicLoad(&(a[min(u32(i), 3u)]));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  var predicated_value : i32;
-  if (predicate) {
-    predicated_value = atomicLoad(&(a[index]));
-  }
-  let r = predicated_value;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, AtomicAnd) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  let i = 0;
-  atomicAnd(&(a[i]), 10);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  let i = 0;
-  atomicAnd(&(a[min(u32(i), 3u)]), 10);
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  if (predicate) {
-    atomicAnd(&(a[index]), 10);
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// workgroupUniformLoad
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, WorkgroupUniformLoad) {
-    auto* src = R"(
-var<workgroup> a : array<u32, 32>;
-
-@compute @workgroup_size(1)
-fn f() {
-  let i = 1;
-  let p = &(a[i]);
-  let v = workgroupUniformLoad(p);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<workgroup> a : array<u32, 32>;
-
-@compute @workgroup_size(1)
-fn f() {
-  let i = 1;
-  let p = &(a[min(u32(i), 31u)]);
-  let v = workgroupUniformLoad(p);
-}
-)",
-                          /* predicate */ R"(
-var<workgroup> a : array<u32, 32>;
-
-@compute @workgroup_size(1)
-fn f() {
-  let i = 1;
-  let index = i;
-  let predicate = (u32(index) <= 31u);
-  let p = &(a[index]);
-  var predicated_value : u32;
-  if (predicate) {
-    predicated_value = workgroupUniformLoad(p);
-  } else {
-    workgroupBarrier();
-  }
-  let v = predicated_value;
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Usage in loops
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, ArrayVal_ForLoopInit) {
-    auto* src = R"(
-fn f() {
-  let a = array<i32, 3>();
-  var v = 1;
-  for(var i = a[v]; (i < 3); i++) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn f() {
-  let a = array<i32, 3>();
-  var v = 1;
-  for(var i = a[min(u32(v), 2u)]; (i < 3); i++) {
-    var in_loop = 42;
-  }
-}
-)",
-                          /* predicate */ R"(
-fn f() {
-  let a = array<i32, 3>();
-  var v = 1;
-  {
-    let index = v;
-    let predicate = (u32(index) <= 2u);
-    var predicated_expr : i32;
-    if (predicate) {
-      predicated_expr = a[index];
-    }
-    var i = predicated_expr;
-    loop {
-      if (!((i < 3))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        i++;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, ArrayVal_ForLoopCond) {
-    auto* src = R"(
-fn f() {
-  let a = array<i32, 3>();
-  var v = 1;
-  for(var i = 0; (i < a[v]); i++) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn f() {
-  let a = array<i32, 3>();
-  var v = 1;
-  for(var i = 0; (i < a[min(u32(v), 2u)]); i++) {
-    var in_loop = 42;
-  }
-}
-)",
-                          /* predicate */ R"(
-fn f() {
-  let a = array<i32, 3>();
-  var v = 1;
-  {
-    var i = 0;
-    loop {
-      let index = v;
-      let predicate = (u32(index) <= 2u);
-      var predicated_expr : i32;
-      if (predicate) {
-        predicated_expr = a[index];
-      }
-      if (!((i < predicated_expr))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        i++;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, ArrayVal_ForLoopCont) {
-    auto* src = R"(
-fn f() {
-  let a = array<i32, 3>();
-  for(var i = 0; (i < 5); i = a[i]) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn f() {
-  let a = array<i32, 3>();
-  for(var i = 0; (i < 5); i = a[min(u32(i), 2u)]) {
-    var in_loop = 42;
-  }
-}
-)",
-                          /* predicate */ R"(
-fn f() {
-  let a = array<i32, 3>();
-  {
-    var i = 0;
-    loop {
-      if (!((i < 5))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        let index = i;
-        let predicate = (u32(index) <= 2u);
-        var predicated_expr : i32;
-        if (predicate) {
-          predicated_expr = a[index];
-        }
-        i = predicated_expr;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_ForLoopInit) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var coords = vec2(1);
-  var level = 1;
-  for(var i = textureLoad(t, coords, level).x; (i < 3); i++) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var coords = vec2(1);
-  var level = 1;
-  {
-    let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-    var i = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx).x;
-    loop {
-      if (!((i < 3))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        i++;
-      }
-    }
-  }
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var coords = vec2(1);
-  var level = 1;
-  {
-    let level_idx = u32(level);
-    let num_levels = textureNumLevels(t);
-    let coords_1 = vec2<u32>(coords);
-    var predicated_value : vec4<i32>;
-    if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
-      predicated_value = textureLoad(t, coords_1, level_idx);
-    }
-    var i = predicated_value.x;
-    loop {
-      if (!((i < 3))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        i++;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_ForLoopCond) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var coords = vec2(1);
-  var level = 1;
-  for(var i = 0; (i < textureLoad(t, coords, level).x); i++) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var coords = vec2(1);
-  var level = 1;
-  {
-    var i = 0;
-    loop {
-      let level_idx = min(u32(level), (textureNumLevels(t) - 1));
-      if (!((i < textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx).x))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        i++;
-      }
-    }
-  }
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var coords = vec2(1);
-  var level = 1;
-  {
-    var i = 0;
-    loop {
-      let level_idx = u32(level);
-      let num_levels = textureNumLevels(t);
-      let coords_1 = vec2<u32>(coords);
-      var predicated_value : vec4<i32>;
-      if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
-        predicated_value = textureLoad(t, coords_1, level_idx);
-      }
-      if (!((i < predicated_value.x))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        i++;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, TextureLoad_ForLoopCont) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var level = 1;
-  for(var i = 0; (i < 5); i = textureLoad(t, vec2(i), i).x) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var level = 1;
-  {
-    var i = 0;
-    loop {
-      if (!((i < 5))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        let level_idx = min(u32(i), (textureNumLevels(t) - 1));
-        i = textureLoad(t, clamp(vec2(i), vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx).x;
-      }
-    }
-  }
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var t : texture_2d<i32>;
-
-fn f() {
-  var level = 1;
-  {
-    var i = 0;
-    loop {
-      if (!((i < 5))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        let level_idx = u32(i);
-        let num_levels = textureNumLevels(t);
-        let coords = vec2<u32>(vec2(i));
-        var predicated_value : vec4<i32>;
-        if (((level_idx < num_levels) & all((coords < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
-          predicated_value = textureLoad(t, coords, level_idx);
-        }
-        i = predicated_value.x;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, AtomicXor_ForLoopInit) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  var i = 0;
-  for(atomicXor(&(a[i]), 4); (i < 3); i++) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  var i = 0;
-  for(atomicXor(&(a[min(u32(i), 3u)]), 4); (i < 3); i++) {
-    var in_loop = 42;
-  }
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  var i = 0;
-  {
-    let index = i;
-    let predicate = (u32(index) <= 3u);
-    if (predicate) {
-      atomicXor(&(a[index]), 4);
-    }
-    loop {
-      if (!((i < 3))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        i++;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, AtomicXor_ForLoopCond) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  for(var i = 0; (i < atomicXor(&(a[i]), 4)); i++) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  for(var i = 0; (i < atomicXor(&(a[min(u32(i), 3u)]), 4)); i++) {
-    var in_loop = 42;
-  }
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  {
-    var i = 0;
-    loop {
-      let index = i;
-      let predicate = (u32(index) <= 3u);
-      var predicated_value : i32;
-      if (predicate) {
-        predicated_value = atomicXor(&(a[index]), 4);
-      }
-      if (!((i < predicated_value))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        i++;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, AtomicXor_ForLoopCont) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  for(var i = 0; (i < 5); i = atomicXor(&(a[i]), 4)) {
-    var in_loop = 42;
-  }
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  for(var i = 0; (i < 5); i = atomicXor(&(a[min(u32(i), 3u)]), 4)) {
-    var in_loop = 42;
-  }
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<atomic<i32>, 4>;
-
-fn f() {
-  {
-    var i = 0;
-    loop {
-      if (!((i < 5))) {
-        break;
-      }
-      {
-        var in_loop = 42;
-      }
-
-      continuing {
-        let index = i;
-        let predicate = (u32(index) <= 3u);
-        var predicated_value : i32;
-        if (predicate) {
-          predicated_value = atomicXor(&(a[index]), 4);
-        }
-        i = predicated_value;
-      }
-    }
-  }
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// User pointer parameters
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_PrivatePointerParameter_IndexWithConstant) {
-    auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  y(1, &(a[1]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) -> i32 {
-  var predicated_expr : i32;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, p_predicate_1 : bool, post : i32) -> i32 {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  y(1, &(a[1]), true, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_WorkgroupPointerParameter_IndexWithConstant) {
-    auto* src = R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  y(1, &(a[1]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) -> i32 {
-  var predicated_expr : i32;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, p_predicate_1 : bool, post : i32) -> i32 {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  y(1, &(a[1]), true, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_UniformPointerParameter_IndexWithConstant) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
-  return x(pre, p, post);
-}
-
-fn z() {
-  y(vec4(1), &(a[1]), vec4(2));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
-  var predicated_expr : vec4<i32>;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate_1 : bool, post : vec4i) -> vec4i {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  y(vec4(1), &(a[1]), true, vec4(2));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_StoragePointerParameter_IndexWithConstant) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
-  return x(pre, p, post);
-}
-
-fn z() {
-  y(vec4(1), &(a[1]), vec4(2));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
-  var predicated_expr : vec4<i32>;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, p_predicate_1 : bool, post : vec4i) -> vec4i {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  y(vec4(1), &(a[1]), true, vec4(2));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_FunctionPointerParameter_IndexWithConstant) {
-    auto* src = R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  y(1, &(a[1]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) -> i32 {
-  var predicated_expr : i32;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, p_predicate_1 : bool, post : i32) -> i32 {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  y(1, &(a[1]), true, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_PrivatePointerParameter_IndexWithLet) {
-    auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[i]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) -> i32 {
-  var predicated_expr : i32;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, p_predicate_1 : bool, post : i32) -> i32 {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(1, &(a[index]), predicate, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_WorkgroupPointerParameter_IndexWithLet) {
-    auto* src = R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[i]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
-                          /* predicate */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) -> i32 {
-  var predicated_expr : i32;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, p_predicate_1 : bool, post : i32) -> i32 {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(1, &(a[index]), predicate, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_UniformPointerParameter_IndexWithLet) {
-    auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
-  return x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(vec4(1), &(a[i]), vec4(2));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, post : vec4i) -> vec4i {
-  return x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(vec4(1), &(a[min(u32(i), 3u)]), vec4(2));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var<uniform> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
-  var predicated_expr : vec4<i32>;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : vec4i, p : ptr<uniform, vec4i>, p_predicate_1 : bool, post : vec4i) -> vec4i {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(vec4(1), &(a[index]), predicate, vec4(2));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_StoragePointerParameter_IndexWithLet) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
-  return x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(vec4(1), &(a[i]), vec4(2));
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, post : vec4i) -> vec4i {
-  return x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(vec4(1), &(a[min(u32(i), 3u)]), vec4(2));
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage> a : array<vec4i, 4>;
-
-fn x(pre : vec4i, p : ptr<storage, vec4i>, p_predicate : bool, post : vec4i) -> vec4i {
-  var predicated_expr : vec4<i32>;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : vec4i, p : ptr<storage, vec4i>, p_predicate_1 : bool, post : vec4i) -> vec4i {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(vec4(1), &(a[index]), predicate, vec4(2));
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_FunctionPointerParameter_IndexWithLet) {
-    auto* src = R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  let i = 0;
-  y(1, &(a[i]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return ((pre + *(p)) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
-  return x(pre, p, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  let i = 0;
-  y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
-                          /* predicate */ R"(
-fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) -> i32 {
-  var predicated_expr : i32;
-  if (p_predicate) {
-    predicated_expr = *(p);
-  }
-  return ((pre + predicated_expr) + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, p_predicate_1 : bool, post : i32) -> i32 {
-  return x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(1, &(a[index]), predicate, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_PrivatePointerParameter_IndexWithConstant) {
-    auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  y(1, &(a[1]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) {
-  if (p_predicate) {
-    *(p) = (pre + post);
-  }
-}
-
-fn y(pre : i32, p : ptr<private, i32>, p_predicate_1 : bool, post : i32) {
-  x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  y(1, &(a[1]), true, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_WorkgroupPointerParameter_IndexWithConstant) {
-    auto* src = R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  y(1, &(a[1]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) {
-  if (p_predicate) {
-    *(p) = (pre + post);
-  }
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, p_predicate_1 : bool, post : i32) {
-  x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  y(1, &(a[1]), true, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_StoragePointerParameter_IndexWithConstant) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  y(1, &(a[1]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, p_predicate : bool, post : i32) {
-  if (p_predicate) {
-    *(p) = (pre + post);
-  }
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, p_predicate_1 : bool, post : i32) {
-  x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  y(1, &(a[1]), true, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_FunctionPointerParameter_IndexWithConstant) {
-    auto* src = R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  y(1, &(a[1]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ src,
-                          /* predicate */ R"(
-fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) {
-  if (p_predicate) {
-    *(p) = (pre + post);
-  }
-}
-
-fn y(pre : i32, p : ptr<function, i32>, p_predicate_1 : bool, post : i32) {
-  x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  y(1, &(a[1]), true, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_PrivatePointerParameter_IndexWithLet) {
-    auto* src = R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[i]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<private, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
-                          /* predicate */ R"(
-var<private> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<private, i32>, p_predicate : bool, post : i32) {
-  if (p_predicate) {
-    *(p) = (pre + post);
-  }
-}
-
-fn y(pre : i32, p : ptr<private, i32>, p_predicate_1 : bool, post : i32) {
-  x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(1, &(a[index]), predicate, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_WorkgroupPointerParameter_IndexWithLet) {
-    auto* src = R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[i]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
-                          /* predicate */ R"(
-var<workgroup> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<workgroup, i32>, p_predicate : bool, post : i32) {
-  if (p_predicate) {
-    *(p) = (pre + post);
-  }
-}
-
-fn y(pre : i32, p : ptr<workgroup, i32>, p_predicate_1 : bool, post : i32) {
-  x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(1, &(a[index]), predicate, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_StoragePointerParameter_IndexWithLet) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[i]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  let i = 0;
-  y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
-                          /* predicate */ R"(
-@group(0) @binding(0) var<storage, read_write> a : array<i32, 4>;
-
-fn x(pre : i32, p : ptr<storage, i32, read_write>, p_predicate : bool, post : i32) {
-  if (p_predicate) {
-    *(p) = (pre + post);
-  }
-}
-
-fn y(pre : i32, p : ptr<storage, i32, read_write>, p_predicate_1 : bool, post : i32) {
-  x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(1, &(a[index]), predicate, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Write_FunctionPointerParameter_IndexWithLet) {
-    auto* src = R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  let i = 0;
-  y(1, &(a[i]), 2);
-}
-)";
-
-    auto* expect = Expect(GetParam(),
-                          /* ignore */ src,
-                          /* clamp */ R"(
-fn x(pre : i32, p : ptr<function, i32>, post : i32) {
-  *(p) = (pre + post);
-}
-
-fn y(pre : i32, p : ptr<function, i32>, post : i32) {
-  x(pre, p, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  let i = 0;
-  y(1, &(a[min(u32(i), 3u)]), 2);
-}
-)",
-                          /* predicate */ R"(
-fn x(pre : i32, p : ptr<function, i32>, p_predicate : bool, post : i32) {
-  if (p_predicate) {
-    *(p) = (pre + post);
-  }
-}
-
-fn y(pre : i32, p : ptr<function, i32>, p_predicate_1 : bool, post : i32) {
-  x(pre, p, p_predicate_1, post);
-}
-
-fn z() {
-  var a : array<i32, 4>;
-  let i = 0;
-  let index = i;
-  let predicate = (u32(index) <= 3u);
-  y(1, &(a[index]), predicate, 2);
-}
-)");
-
-    auto got = Run<Robustness>(src, Config(GetParam()));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// disable_runtime_sized_array_index_clamping == true
-////////////////////////////////////////////////////////////////////////////////
-
-TEST_P(RobustnessTest, Read_disable_unsized_array_index_clamping_i32) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
-  let i = 25i;
-  var d : f32 = s[i];
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
-  let i = 25i;
-  var d : f32 = s[u32(i)];
-}
-)";
-
-    auto got = Run<Robustness>(src, Config(GetParam(), true));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_disable_unsized_array_index_clamping_u32) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
-  let i = 25u;
-  var d : f32 = s[i];
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam(), true));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_disable_unsized_array_index_clamping_abstract_int) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
-  var d : f32 = s[25];
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
-  var d : f32 = s[u32(25)];
-}
-)";
-
-    auto got = Run<Robustness>(src, Config(GetParam(), true));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Read_disable_unsized_array_index_clamping_abstract_int_ViaPointerIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
-  let p = &(s);
-  var d : f32 = p[25];
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage, read> s : array<f32>;
-
-fn f() {
-  let p = &(s);
-  var d : f32 = p[u32(25)];
-}
-)";
-
-    auto got = Run<Robustness>(src, Config(GetParam(), true));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_disable_unsized_array_index_clamping_i32) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
-  let i = 25i;
-  s[i] = 0.5f;
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
-  let i = 25i;
-  s[u32(i)] = 0.5f;
-}
-)";
-
-    auto got = Run<Robustness>(src, Config(GetParam(), true));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_disable_unsized_array_index_clamping_u32) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
-  let i = 25u;
-  s[i] = 0.5f;
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<Robustness>(src, Config(GetParam(), true));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_disable_unsized_array_index_clamping_abstract_int) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
-  s[25] = 0.5f;
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
-  s[u32(25)] = 0.5f;
-}
-)";
-
-    auto got = Run<Robustness>(src, Config(GetParam(), true));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(RobustnessTest, Assign_disable_unsized_array_index_clamping_abstract_int_ViaPointerIndex) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
-  let p = &(s);
-  p[25] = 0.5f;
-}
-)";
-
-    auto* expect = R"(
-@group(0) @binding(0) var<storage, read_write> s : array<f32>;
-
-fn f() {
-  let p = &(s);
-  p[u32(25)] = 0.5f;
-}
-)";
-
-    auto got = Run<Robustness>(src, Config(GetParam(), true));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(,
-                         RobustnessTest,
-                         testing::Values(Robustness::Action::kIgnore,
-                                         Robustness::Action::kClamp,
-                                         Robustness::Action::kPredicate));
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/single_entry_point.cc b/src/tint/lang/wgsl/ast/transform/single_entry_point.cc
deleted file mode 100644
index a9673f7..0000000
--- a/src/tint/lang/wgsl/ast/transform/single_entry_point.cc
+++ /dev/null
@@ -1,148 +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/wgsl/ast/transform/single_entry_point.h"
-
-#include <unordered_set>
-#include <utility>
-
-#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.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SingleEntryPoint);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SingleEntryPoint::Config);
-
-namespace tint::ast::transform {
-
-SingleEntryPoint::SingleEntryPoint() = default;
-
-SingleEntryPoint::~SingleEntryPoint() = default;
-
-Transform::ApplyResult SingleEntryPoint::Apply(const Program& src,
-                                               const DataMap& inputs,
-                                               DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    auto* cfg = inputs.Get<Config>();
-    if (cfg == nullptr) {
-        b.Diagnostics().AddError(Source{}) << "missing transform data for " << TypeInfo().name;
-        return resolver::Resolve(b);
-    }
-
-    // Find the target entry point.
-    const Function* entry_point = nullptr;
-    for (auto* f : src.AST().Functions()) {
-        if (!f->IsEntryPoint()) {
-            continue;
-        }
-        if (f->name->symbol.Name() == cfg->entry_point_name) {
-            entry_point = f;
-            break;
-        }
-    }
-    if (entry_point == nullptr) {
-        b.Diagnostics().AddError(Source{})
-            << "entry point '" << cfg->entry_point_name << "' not found";
-        return resolver::Resolve(b);
-    }
-
-    auto& sem = src.Sem();
-    auto& referenced_vars = sem.Get(entry_point)->TransitivelyReferencedGlobals();
-
-    // Clone any module-scope variables, types, and functions that are statically referenced by the
-    // target entry point.
-    for (auto* decl : src.AST().GlobalDeclarations()) {
-        Switch(
-            decl,  //
-            [&](const TypeDecl* ty) {
-                // Strip aliases that reference unused override declarations.
-                if (auto* arr = sem.Get(ty)->As<sem::Array>()) {
-                    for (auto* o : arr->TransitivelyReferencedOverrides()) {
-                        if (!referenced_vars.Contains(o)) {
-                            return;
-                        }
-                    }
-                }
-
-                // TODO(jrprice): Strip other unused types.
-                b.AST().AddTypeDecl(ctx.Clone(ty));
-            },
-            [&](const Override* override) {
-                if (referenced_vars.Contains(sem.Get(override))) {
-                    if (!HasAttribute<IdAttribute>(override->attributes)) {
-                        // If the override doesn't already have an @id() attribute, add one
-                        // so that its allocated ID so that it won't be affected by other
-                        // stripped away overrides
-                        auto* global = sem.Get(override);
-                        const auto* id = b.Id(global->Attributes().override_id.value());
-                        ctx.InsertFront(override->attributes, id);
-                    }
-                    b.AST().AddGlobalVariable(ctx.Clone(override));
-                }
-            },
-            [&](const Var* var) {
-                if (referenced_vars.Contains(sem.Get<sem::GlobalVariable>(var))) {
-                    b.AST().AddGlobalVariable(ctx.Clone(var));
-                }
-            },
-            [&](const Const* c) {
-                // Always keep 'const' declarations, as these can be used by attributes and array
-                // sizes, which are not tracked as transitively used by functions. They also don't
-                // typically get emitted by the backend unless they're actually used.
-                b.AST().AddGlobalVariable(ctx.Clone(c));
-            },
-            [&](const Function* func) {
-                if (sem.Get(func)->HasCallGraphEntryPoint(entry_point->name->symbol)) {
-                    b.AST().AddFunction(ctx.Clone(func));
-                }
-            },
-            [&](const Enable* ext) { b.AST().AddEnable(ctx.Clone(ext)); },
-            [&](const Requires*) {
-                // Drop requires directives as they are optional, and it's non-trivial to determine
-                // which features are needed for which entry points.
-            },
-            [&](const ConstAssert* ca) { b.AST().AddConstAssert(ctx.Clone(ca)); },
-            [&](const DiagnosticDirective* d) { b.AST().AddDiagnosticDirective(ctx.Clone(d)); },  //
-            TINT_ICE_ON_NO_MATCH);
-    }
-
-    return resolver::Resolve(b);
-}
-
-SingleEntryPoint::Config::Config(std::string entry_point) : entry_point_name(entry_point) {}
-
-SingleEntryPoint::Config::Config(const Config&) = default;
-SingleEntryPoint::Config::~Config() = default;
-SingleEntryPoint::Config& SingleEntryPoint::Config::operator=(const Config&) = default;
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/single_entry_point.h b/src/tint/lang/wgsl/ast/transform/single_entry_point.h
deleted file mode 100644
index 39d1943..0000000
--- a/src/tint/lang/wgsl/ast/transform/single_entry_point.h
+++ /dev/null
@@ -1,77 +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_WGSL_AST_TRANSFORM_SINGLE_ENTRY_POINT_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_SINGLE_ENTRY_POINT_H_
-
-#include <string>
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// Strip all but one entry point a module.
-///
-/// All module-scope variables, types, and functions that are not used by the
-/// target entry point will also be removed.
-class SingleEntryPoint final : public Castable<SingleEntryPoint, Transform> {
-  public:
-    /// Configuration options for the transform
-    struct Config final : public Castable<Config, Data> {
-        /// Constructor
-        /// @param entry_point the name of the entry point to keep
-        explicit Config(std::string entry_point = "");
-
-        /// Copy constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// Assignment operator
-        /// @returns this Config
-        Config& operator=(const Config&);
-
-        /// The name of the entry point to keep.
-        std::string entry_point_name;
-    };
-
-    /// Constructor
-    SingleEntryPoint();
-
-    /// Destructor
-    ~SingleEntryPoint() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_SINGLE_ENTRY_POINT_H_
diff --git a/src/tint/lang/wgsl/ast/transform/single_entry_point_test.cc b/src/tint/lang/wgsl/ast/transform/single_entry_point_test.cc
deleted file mode 100644
index 4d4ede3..0000000
--- a/src/tint/lang/wgsl/ast/transform/single_entry_point_test.cc
+++ /dev/null
@@ -1,718 +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/wgsl/ast/transform/single_entry_point.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using SingleEntryPointTest = TransformTest;
-
-TEST_F(SingleEntryPointTest, Error_MissingTransformData) {
-    auto* src = "";
-
-    auto* expect = "error: missing transform data for tint::ast::transform::SingleEntryPoint";
-
-    auto got = Run<SingleEntryPoint>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Error_NoEntryPoints) {
-    auto* src = "";
-
-    auto* expect = "error: entry point 'main' not found";
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>("main");
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Error_InvalidEntryPoint) {
-    auto* src = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = "error: entry point '_' not found";
-
-    SingleEntryPoint::Config cfg("_");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Error_NotAnEntryPoint) {
-    auto* src = R"(
-fn foo() {}
-
-@fragment
-fn main() {}
-)";
-
-    auto* expect = "error: entry point 'foo' not found";
-
-    SingleEntryPoint::Config cfg("foo");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, SingleEntryPoint) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
-    SingleEntryPoint::Config cfg("main");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(src, str(got));
-}
-
-TEST_F(SingleEntryPointTest, MultipleEntryPoints) {
-    auto* src = R"(
-@vertex
-fn vert_main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-
-@fragment
-fn frag_main() {
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
-}
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn comp_main1() {
-}
-)";
-
-    SingleEntryPoint::Config cfg("comp_main1");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, GlobalVariables) {
-    auto* src = R"(
-var<private> a : f32;
-
-var<private> b : f32;
-
-var<private> c : f32;
-
-var<private> d : f32;
-
-@vertex
-fn vert_main() -> @builtin(position) vec4<f32> {
-  a = 0.0;
-  return vec4<f32>();
-}
-
-@fragment
-fn frag_main() {
-  b = 0.0;
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  c = 0.0;
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
-  d = 0.0;
-}
-)";
-
-    auto* expect = R"(
-var<private> c : f32;
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  c = 0.0;
-}
-)";
-
-    SingleEntryPoint::Config cfg("comp_main1");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, GlobalConstants) {
-    auto* src = R"(
-const a : f32 = 1.0;
-
-const b : f32 = 1.0;
-
-const c : f32 = 1.0;
-
-const d : f32 = 1.0;
-
-@vertex
-fn vert_main() -> @builtin(position) vec4<f32> {
-  let local_a : f32 = a;
-  return vec4<f32>();
-}
-
-@fragment
-fn frag_main() {
-  let local_b : f32 = b;
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  let local_c : f32 = c;
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
-  let local_d : f32 = d;
-}
-)";
-
-    auto* expect = R"(
-const a : f32 = 1.0;
-
-const b : f32 = 1.0;
-
-const c : f32 = 1.0;
-
-const d : f32 = 1.0;
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  let local_c : f32 = c;
-}
-)";
-
-    SingleEntryPoint::Config cfg("comp_main1");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, WorkgroupSizeConstPreserved) {
-    auto* src = R"(
-const size : i32 = 1;
-
-@compute @workgroup_size(size)
-fn main() {
-}
-)";
-
-    auto* expect = src;
-
-    SingleEntryPoint::Config cfg("main");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, OverridableConstants) {
-    auto* src = R"(
-@id(1001) override c1 : u32 = 1u;
-          override c2 : u32 = 1u;
-@id(0)    override c3 : u32 = 1u;
-@id(9999) override c4 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-    let local_d = c1;
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
-    let local_d = c2;
-}
-
-@compute @workgroup_size(1)
-fn comp_main3() {
-    let local_d = c3;
-}
-
-@compute @workgroup_size(1)
-fn comp_main4() {
-    let local_d = c4;
-}
-
-@compute @workgroup_size(1)
-fn comp_main5() {
-    let local_d = 1u;
-}
-)";
-
-    {
-        SingleEntryPoint::Config cfg("comp_main1");
-        auto* expect = R"(
-@id(1001) override c1 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  let local_d = c1;
-}
-)";
-        DataMap data;
-        data.Add<SingleEntryPoint::Config>(cfg);
-        auto got = Run<SingleEntryPoint>(src, data);
-        EXPECT_EQ(expect, str(got));
-    }
-
-    {
-        SingleEntryPoint::Config cfg("comp_main2");
-        // The decorator is replaced with the one with explicit id
-        // And should not be affected by other constants stripped away
-        auto* expect = R"(
-@id(1) override c2 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main2() {
-  let local_d = c2;
-}
-)";
-        DataMap data;
-        data.Add<SingleEntryPoint::Config>(cfg);
-        auto got = Run<SingleEntryPoint>(src, data);
-        EXPECT_EQ(expect, str(got));
-    }
-
-    {
-        SingleEntryPoint::Config cfg("comp_main3");
-        auto* expect = R"(
-@id(0) override c3 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main3() {
-  let local_d = c3;
-}
-)";
-        DataMap data;
-        data.Add<SingleEntryPoint::Config>(cfg);
-        auto got = Run<SingleEntryPoint>(src, data);
-        EXPECT_EQ(expect, str(got));
-    }
-
-    {
-        SingleEntryPoint::Config cfg("comp_main4");
-        auto* expect = R"(
-@id(9999) override c4 : u32 = 1u;
-
-@compute @workgroup_size(1)
-fn comp_main4() {
-  let local_d = c4;
-}
-)";
-        DataMap data;
-        data.Add<SingleEntryPoint::Config>(cfg);
-        auto got = Run<SingleEntryPoint>(src, data);
-        EXPECT_EQ(expect, str(got));
-    }
-
-    {
-        SingleEntryPoint::Config cfg("comp_main5");
-        auto* expect = R"(
-@compute @workgroup_size(1)
-fn comp_main5() {
-  let local_d = 1u;
-}
-)";
-        DataMap data;
-        data.Add<SingleEntryPoint::Config>(cfg);
-        auto got = Run<SingleEntryPoint>(src, data);
-        EXPECT_EQ(expect, str(got));
-    }
-}
-
-TEST_F(SingleEntryPointTest, OverridableConstants_TransitiveUses) {
-    // Make sure we do not strip away transitive uses of overridable constants.
-    auto* src = R"(
-@id(0) override c0 : u32;
-
-@id(1) override c1 : u32 = (2 * c0);
-
-@id(2) override c2 : u32;
-
-@id(3) override c3 : u32 = (2 * c2);
-
-@id(4) override c4 : u32;
-
-@id(5) override c5 : u32 = (2 * c4);
-
-alias arr_ty = array<i32, (2 * c5)>;
-
-var<workgroup> arr : arr_ty;
-
-@compute @workgroup_size(1, 1, (2 * c3))
-fn main() {
-  let local_d = c1;
-  arr[0] = 42;
-}
-)";
-
-    auto* expect = src;
-
-    SingleEntryPoint::Config cfg("main");
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, OverridableConstants_UnusedAliasForOverrideSizedArray) {
-    // Make sure we strip away aliases that reference unused overridable constants.
-    auto* src = R"(
-@id(0) override c0 : u32;
-
-// This is all unused by the target entry point.
-@id(1) override c1 : u32;
-alias arr_ty = array<i32, c1>;
-var<workgroup> arr : arr_ty;
-
-@compute @workgroup_size(64)
-fn unused() {
-  arr[0] = 42;
-}
-
-@compute @workgroup_size(64)
-fn main() {
-  let local_d = c0;
-}
-)";
-
-    auto* expect = R"(
-@id(0) override c0 : u32;
-
-@compute @workgroup_size(64)
-fn main() {
-  let local_d = c0;
-}
-)";
-
-    SingleEntryPoint::Config cfg("main");
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, CalledFunctions) {
-    auto* src = R"(
-fn inner1() {
-}
-
-fn inner2() {
-}
-
-fn inner_shared() {
-}
-
-fn outer1() {
-  inner1();
-  inner_shared();
-}
-
-fn outer2() {
-  inner2();
-  inner_shared();
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  outer1();
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
-  outer2();
-}
-)";
-
-    auto* expect = R"(
-fn inner1() {
-}
-
-fn inner_shared() {
-}
-
-fn outer1() {
-  inner1();
-  inner_shared();
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  outer1();
-}
-)";
-
-    SingleEntryPoint::Config cfg("comp_main1");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, GlobalsReferencedByCalledFunctions) {
-    auto* src = R"(
-var<private> inner1_var : f32;
-
-var<private> inner2_var : f32;
-
-var<private> inner_shared_var : f32;
-
-var<private> outer1_var : f32;
-
-var<private> outer2_var : f32;
-
-fn inner1() {
-  inner1_var = 0.0;
-}
-
-fn inner2() {
-  inner2_var = 0.0;
-}
-
-fn inner_shared() {
-  inner_shared_var = 0.0;
-}
-
-fn outer1() {
-  inner1();
-  inner_shared();
-  outer1_var = 0.0;
-}
-
-fn outer2() {
-  inner2();
-  inner_shared();
-  outer2_var = 0.0;
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  outer1();
-}
-
-@compute @workgroup_size(1)
-fn comp_main2() {
-  outer2();
-}
-)";
-
-    auto* expect = R"(
-var<private> inner1_var : f32;
-
-var<private> inner_shared_var : f32;
-
-var<private> outer1_var : f32;
-
-fn inner1() {
-  inner1_var = 0.0;
-}
-
-fn inner_shared() {
-  inner_shared_var = 0.0;
-}
-
-fn outer1() {
-  inner1();
-  inner_shared();
-  outer1_var = 0.0;
-}
-
-@compute @workgroup_size(1)
-fn comp_main1() {
-  outer1();
-}
-)";
-
-    SingleEntryPoint::Config cfg("comp_main1");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, GlobalConstUsedAsArraySize) {
-    // See crbug.com/tint/1598
-    auto* src = R"(
-const MY_SIZE = 5u;
-
-alias Arr = array<i32, MY_SIZE>;
-
-@fragment
-fn main() {
-}
-)";
-
-    auto* expect = src;
-
-    SingleEntryPoint::Config cfg("main");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Directives) {
-    // Make sure that directives are preserved.
-    auto* src = R"(
-enable f16;
-diagnostic(off, derivative_uniformity);
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
-    SingleEntryPoint::Config cfg("main");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(src, str(got));
-}
-
-TEST_F(SingleEntryPointTest, Requires) {
-    // Make sure that requires are handled (and dropped).
-    auto* src = R"(
-requires readonly_and_readwrite_storage_textures;
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
-    SingleEntryPoint::Config cfg("main");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, ConstAssert_ModuleScope) {
-    // module scope const_assert is preserved
-    auto* src = R"(
-const C = 42;
-
-const_assert (C == 42);
-
-@compute @workgroup_size(1)
-fn main() {
-}
-)";
-
-    auto* expect = src;
-
-    SingleEntryPoint::Config cfg("main");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SingleEntryPointTest, ConstAssert_FnScope) {
-    // function scope const_assert is preserved
-    auto* src = R"(
-const C = 42;
-
-@compute @workgroup_size(1)
-fn main() {
-  const_assert (C == 42);
-}
-)";
-
-    auto* expect = src;
-
-    SingleEntryPoint::Config cfg("main");
-
-    DataMap data;
-    data.Add<SingleEntryPoint::Config>(cfg);
-    auto got = Run<SingleEntryPoint>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/substitute_override.cc b/src/tint/lang/wgsl/ast/transform/substitute_override.cc
deleted file mode 100644
index 477a7fb..0000000
--- a/src/tint/lang/wgsl/ast/transform/substitute_override.cc
+++ /dev/null
@@ -1,146 +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/wgsl/ast/transform/substitute_override.h"
-
-#include <functional>
-#include <utility>
-
-#include "src/tint/lang/core/builtin_fn.h"
-#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/builtin_fn.h"
-#include "src/tint/lang/wgsl/sem/index_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/rtti/switch.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SubstituteOverride);
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SubstituteOverride::Config);
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
-    for (auto* node : program.AST().GlobalVariables()) {
-        if (node->Is<Override>()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
-SubstituteOverride::SubstituteOverride() = default;
-
-SubstituteOverride::~SubstituteOverride() = default;
-
-Transform::ApplyResult SubstituteOverride::Apply(const Program& src,
-                                                 const DataMap& config,
-                                                 DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    const auto* data = config.Get<Config>();
-    if (!data) {
-        b.Diagnostics().AddError(Source{}) << "Missing override substitution data";
-        return resolver::Resolve(b);
-    }
-
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ctx.ReplaceAll([&](const Override* w) -> const Const* {
-        auto* sem = ctx.src->Sem().Get(w);
-
-        auto source = ctx.Clone(w->source);
-        auto sym = ctx.Clone(w->name->symbol);
-        Type ty = w->type ? ctx.Clone(w->type) : Type{};
-
-        // No replacement provided, just clone the override node as a const.
-        auto iter = data->map.find(sem->Attributes().override_id.value());
-        if (iter == data->map.end()) {
-            if (!w->initializer) {
-                b.Diagnostics().AddError(Source{})
-                    << "Initializer not provided for override, and override not overridden.";
-                return nullptr;
-            }
-            return b.Const(source, sym, ty, ctx.Clone(w->initializer));
-        }
-
-        auto value = iter->second;
-        auto* ctor = Switch(
-            sem->Type(),
-            [&](const core::type::Bool*) { return b.Expr(!std::equal_to<double>()(value, 0.0)); },
-            [&](const core::type::I32*) { return b.Expr(i32(value)); },
-            [&](const core::type::U32*) { return b.Expr(u32(value)); },
-            [&](const core::type::F32*) { return b.Expr(f32(value)); },
-            [&](const core::type::F16*) { return b.Expr(f16(value)); });
-
-        if (!ctor) {
-            b.Diagnostics().AddError(Source{}) << "Failed to create override-expression";
-            return nullptr;
-        }
-
-        return b.Const(source, sym, ty, ctor);
-    });
-
-    // Ensure that objects that are indexed with an override-expression are materialized.
-    // If the object is not materialized, and the 'override' variable is turned to a 'const', the
-    // resulting type of the index may change. See: crbug.com/tint/1697.
-    ctx.ReplaceAll([&](const IndexAccessorExpression* expr) -> const IndexAccessorExpression* {
-        if (auto* sem = src.Sem().Get(expr)) {
-            if (auto* access = sem->UnwrapMaterialize()->As<sem::IndexAccessorExpression>()) {
-                if (access->Object()->UnwrapMaterialize()->Type()->IsAbstract() &&
-                    access->Index()->Stage() == core::EvaluationStage::kOverride) {
-                    auto* obj = b.Call(wgsl::str(wgsl::BuiltinFn::kTintMaterialize),
-                                       ctx.Clone(expr->object));
-                    return b.IndexAccessor(obj, ctx.Clone(expr->index));
-                }
-            }
-        }
-        return nullptr;
-    });
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-SubstituteOverride::Config::Config() = default;
-
-SubstituteOverride::Config::Config(const Config&) = default;
-
-SubstituteOverride::Config::~Config() = default;
-
-SubstituteOverride::Config& SubstituteOverride::Config::operator=(const Config&) = default;
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/substitute_override.h b/src/tint/lang/wgsl/ast/transform/substitute_override.h
deleted file mode 100644
index a49bdfb..0000000
--- a/src/tint/lang/wgsl/ast/transform/substitute_override.h
+++ /dev/null
@@ -1,99 +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_WGSL_AST_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/api/common/override_id.h"
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-#include "src/tint/utils/reflection.h"
-
-namespace tint::ast::transform {
-
-/// A transform that replaces overrides with the constant values provided.
-///
-/// # Example
-/// ```
-/// override width: f32;
-/// @id(1) override height: i32 = 4;
-/// override depth = 1i;
-/// ```
-///
-/// When transformed with `width` -> 1, `1` -> 22, `depth` -> 42
-///
-/// ```
-/// const width: f32 = 1f;
-/// const height: i32 = 22i;
-/// const depth = 42i;
-/// ```
-///
-/// @see crbug.com/tint/1582
-class SubstituteOverride final : public Castable<SubstituteOverride, Transform> {
-  public:
-    /// Configuration options for the transform
-    struct Config final : public Castable<Config, Data> {
-        /// Constructor
-        Config();
-
-        /// Copy constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// Assignment operator
-        /// @returns this Config
-        Config& operator=(const Config&);
-
-        /// The map of override identifier to the override value.
-        /// The value is always a double coming into the transform and will be
-        /// converted to the correct type through and initializer.
-        std::unordered_map<OverrideId, double> map;
-
-        /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-        TINT_REFLECT(Config, map);
-    };
-
-    /// Constructor
-    SubstituteOverride();
-
-    /// Destructor
-    ~SubstituteOverride() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
diff --git a/src/tint/lang/wgsl/ast/transform/substitute_override_test.cc b/src/tint/lang/wgsl/ast/transform/substitute_override_test.cc
deleted file mode 100644
index c0204aa..0000000
--- a/src/tint/lang/wgsl/ast/transform/substitute_override_test.cc
+++ /dev/null
@@ -1,335 +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/wgsl/ast/transform/substitute_override.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-TEST(TintCheckAllFieldsReflected, WgslAstTransformSubstituteOverrideTest) {
-    TINT_ASSERT_ALL_FIELDS_REFLECTED(SubstituteOverride::Config);
-}
-
-using SubstituteOverrideTest = TransformTest;
-
-TEST_F(SubstituteOverrideTest, Error_NoData) {
-    auto* src = R"(
-override width: i32;
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = "error: Missing override substitution data";
-
-    DataMap data;
-    auto got = Run<SubstituteOverride>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, Error_NoOverrideValue) {
-    auto* src = R"(
-override width: i32;
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = "error: Initializer not provided for override, and override not overridden.";
-
-    SubstituteOverride::Config cfg;
-    DataMap data;
-    data.Add<SubstituteOverride::Config>(cfg);
-
-    auto got = Run<SubstituteOverride>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, Module_NoOverrides) {
-    auto* src = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    SubstituteOverride::Config cfg;
-
-    DataMap data;
-    data.Add<SubstituteOverride::Config>(cfg);
-    auto got = Run<SubstituteOverride>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, OverrideNotInFile) {
-    auto* src = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    SubstituteOverride::Config cfg;
-    cfg.map.insert({OverrideId{99}, 42.0});
-
-    DataMap data;
-    data.Add<SubstituteOverride::Config>(cfg);
-    auto got = Run<SubstituteOverride>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, ImplicitId) {
-    auto* src = R"(
-enable f16;
-
-override i_width: i32;
-override i_height = 1i;
-
-override f_width: f32;
-override f_height = 1.f;
-
-override h_width: f16;
-override h_height = 1.h;
-
-override b_width: bool;
-override b_height = true;
-
-override o_width = 2i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-const i_width : i32 = 42i;
-
-const i_height = 11i;
-
-const f_width : f32 = 22.299999237060546875f;
-
-const f_height = 12.3999996185302734375f;
-
-const h_width : f16 = 9.3984375h;
-
-const h_height = 3.3984375h;
-
-const b_width : bool = true;
-
-const b_height = false;
-
-const o_width = 2i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    SubstituteOverride::Config cfg;
-    cfg.map.insert({OverrideId{0}, 42.0});
-    cfg.map.insert({OverrideId{1}, 11.0});
-    cfg.map.insert({OverrideId{2}, 22.3});
-    cfg.map.insert({OverrideId{3}, 12.4});
-    cfg.map.insert({OverrideId{4}, 9.4});
-    cfg.map.insert({OverrideId{5}, 3.4});
-    cfg.map.insert({OverrideId{6}, 1.0});
-    cfg.map.insert({OverrideId{7}, 0.0});
-
-    DataMap data;
-    data.Add<SubstituteOverride::Config>(cfg);
-    auto got = Run<SubstituteOverride>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, ExplicitId) {
-    auto* src = R"(
-enable f16;
-
-@id(0) override i_width: i32;
-@id(10) override i_height = 1i;
-
-@id(1) override f_width: f32;
-@id(9) override f_height = 1.f;
-
-@id(2) override h_width: f16;
-@id(8) override h_height = 1.h;
-
-@id(3) override b_width: bool;
-@id(7) override b_height = true;
-
-@id(5) override o_width = 2i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-const i_width : i32 = 42i;
-
-const i_height = 11i;
-
-const f_width : f32 = 22.299999237060546875f;
-
-const f_height = 12.3999996185302734375f;
-
-const h_width : f16 = 9.3984375h;
-
-const h_height = 3.3984375h;
-
-const b_width : bool = true;
-
-const b_height = false;
-
-const o_width = 13i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    SubstituteOverride::Config cfg;
-    cfg.map.insert({OverrideId{0}, 42.0});
-    cfg.map.insert({OverrideId{10}, 11.0});
-    cfg.map.insert({OverrideId{1}, 22.3});
-    cfg.map.insert({OverrideId{9}, 12.4});
-    cfg.map.insert({OverrideId{2}, 9.4});
-    cfg.map.insert({OverrideId{8}, 3.4});
-    cfg.map.insert({OverrideId{3}, 1.0});
-    cfg.map.insert({OverrideId{7}, 0.0});
-    cfg.map.insert({OverrideId{5}, 13});
-
-    DataMap data;
-    data.Add<SubstituteOverride::Config>(cfg);
-    auto got = Run<SubstituteOverride>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, Identifier_Expression) {
-    auto* src = R"(
-override i_height = ~2i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    auto* expect = R"(
-const i_height = 11i;
-
-@vertex
-fn main() -> @builtin(position) vec4<f32> {
-  return vec4<f32>();
-}
-)";
-
-    SubstituteOverride::Config cfg;
-    cfg.map.insert({OverrideId{0}, 11.0});
-
-    DataMap data;
-    data.Add<SubstituteOverride::Config>(cfg);
-    auto got = Run<SubstituteOverride>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(SubstituteOverrideTest, IndexMaterialization) {
-    auto* src = R"(
-override O = 0; // Try switching to 'const'
-
-fn f() {
-  const smaller_than_any_f32 = 1e-50;
-  const large_float = 1e27;
-  // When O is an override, the outer index value is not constant, so the
-  // value is not calculated at shader-creation time, and does not error.
-  //
-  // When O is a const, and 'smaller_than_any_f32' *is not* materialized, the
-  // outer index value will evaluate to 10000, resulting in an out-of-bounds
-  // error.
-  //
-  // When O is a const, and 'smaller_than_any_f32' *is* materialized, the
-  // materialization of 'smaller_than_any_f32' to f32 will evaluate to zero,
-  // and so the outer index value will be zero, and we get no error.
-  _ = vec2(0)[i32(vec2(smaller_than_any_f32)[O]*large_float*large_float)];
-}
-)";
-
-    auto* expect = R"(
-const O = 0i;
-
-fn f() {
-  const smaller_than_any_f32 = 1e-50;
-  const large_float = 1000000000000000013287555072.0;
-  _ = __tint_materialize(vec2(0))[i32(((__tint_materialize(vec2(smaller_than_any_f32))[O] * large_float) * large_float))];
-}
-)";
-
-    SubstituteOverride::Config cfg;
-    cfg.map.insert({OverrideId{0}, 0.0});
-
-    DataMap data;
-    data.Add<SubstituteOverride::Config>(cfg);
-    auto got = Run<SubstituteOverride>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.cc b/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.cc
deleted file mode 100644
index 18e4ff7..0000000
--- a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.cc
+++ /dev/null
@@ -1,160 +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/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
-
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/lang/core/type/abstract_numeric.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/value_constructor.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-#include "src/tint/utils/containers/map.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::VectorizeScalarMatrixInitializers);
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
-    for (auto* node : program.ASTNodes().Objects()) {
-        if (auto* call = program.Sem().Get<sem::Call>(node)) {
-            if (call->Target()->Is<sem::ValueConstructor>() &&
-                call->Type()->Is<core::type::Matrix>()) {
-                auto& args = call->Arguments();
-                if (!args.IsEmpty() && args[0]->Type()->UnwrapRef()->Is<core::type::Scalar>()) {
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
-VectorizeScalarMatrixInitializers::VectorizeScalarMatrixInitializers() = default;
-
-VectorizeScalarMatrixInitializers::~VectorizeScalarMatrixInitializers() = default;
-
-Transform::ApplyResult VectorizeScalarMatrixInitializers::Apply(const Program& src,
-                                                                const DataMap&,
-                                                                DataMap&) const {
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    std::unordered_map<const core::type::Matrix*, Symbol> scalar_inits;
-
-    ctx.ReplaceAll([&](const CallExpression* expr) -> const CallExpression* {
-        auto* call = src.Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
-        auto* ty_init = call->Target()->As<sem::ValueConstructor>();
-        if (!ty_init) {
-            return nullptr;
-        }
-        auto* mat_type = call->Type()->As<core::type::Matrix>();
-        if (!mat_type) {
-            return nullptr;
-        }
-
-        auto& args = call->Arguments();
-        if (args.IsEmpty()) {
-            return nullptr;
-        }
-
-        // If the argument type is a matrix, then this is an identity / conversion initializer.
-        // If the argument type is a vector, then we're already column vectors.
-        // If the argument type is abstract, then we're const-expression and there's no need to
-        // adjust this, as it'll be constant folded by the backend.
-        if (args[0]
-                ->Type()
-                ->UnwrapRef()
-                ->IsAnyOf<core::type::Matrix, core::type::Vector, core::type::AbstractNumeric>()) {
-            return nullptr;
-        }
-
-        // Constructs a matrix using vector columns, with the elements constructed using the
-        // 'element(uint32_t c, uint32_t r)' callback.
-        auto build_mat = [&](auto&& element) {
-            tint::Vector<const Expression*, 4> columns;
-            for (uint32_t c = 0; c < mat_type->Columns(); c++) {
-                tint::Vector<const Expression*, 4> row_values;
-                for (uint32_t r = 0; r < mat_type->Rows(); r++) {
-                    row_values.Push(element(c, r));
-                }
-
-                // Construct the column vector.
-                columns.Push(b.vec(CreateASTTypeFor(ctx, mat_type->Type()), mat_type->Rows(),
-                                   std::move(row_values)));
-            }
-            return b.Call(CreateASTTypeFor(ctx, mat_type), columns);
-        };
-
-        if (args.Length() == 1) {
-            // Generate a helper function for constructing the matrix.
-            // This is done to ensure that the single argument value is only evaluated once, and
-            // with the correct expression evaluation order.
-            auto fn = tint::GetOrAdd(scalar_inits, mat_type, [&] {
-                auto name = b.Symbols().New("build_mat" + std::to_string(mat_type->Columns()) +
-                                            "x" + std::to_string(mat_type->Rows()));
-                b.Func(name,
-                       tint::Vector{
-                           // Single scalar parameter
-                           b.Param("value", CreateASTTypeFor(ctx, mat_type->Type())),
-                       },
-                       CreateASTTypeFor(ctx, mat_type),
-                       tint::Vector{
-                           b.Return(build_mat([&](uint32_t, uint32_t) {  //
-                               return b.Expr("value");
-                           })),
-                       });
-                return name;
-            });
-            return b.Call(fn, ctx.Clone(args[0]->Declaration()));
-        }
-
-        if (DAWN_LIKELY(args.Length() == mat_type->Columns() * mat_type->Rows())) {
-            return build_mat([&](uint32_t c, uint32_t r) {
-                return ctx.Clone(args[c * mat_type->Rows() + r]->Declaration());
-            });
-        }
-
-        TINT_ICE() << "matrix initializer has unexpected number of arguments";
-    });
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h b/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h
deleted file mode 100644
index a7dcf75..0000000
--- a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h
+++ /dev/null
@@ -1,53 +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_WGSL_AST_TRANSFORM_VECTORIZE_SCALAR_MATRIX_INITIALIZERS_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_VECTORIZE_SCALAR_MATRIX_INITIALIZERS_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// A transform that converts scalar matrix initializers to the vector form.
-class VectorizeScalarMatrixInitializers final
-    : public Castable<VectorizeScalarMatrixInitializers, Transform> {
-  public:
-    /// Constructor
-    VectorizeScalarMatrixInitializers();
-
-    /// Destructor
-    ~VectorizeScalarMatrixInitializers() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_VECTORIZE_SCALAR_MATRIX_INITIALIZERS_H_
diff --git a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_test.cc b/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_test.cc
deleted file mode 100644
index fd69553..0000000
--- a/src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers_test.cc
+++ /dev/null
@@ -1,219 +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/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
-
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/utils/text/string.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using VectorizeScalarMatrixInitializersTest = TransformTestWithParam<std::pair<uint32_t, uint32_t>>;
-
-TEST_F(VectorizeScalarMatrixInitializersTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-}
-
-TEST_P(VectorizeScalarMatrixInitializersTest, MultipleScalars) {
-    uint32_t cols = GetParam().first;
-    uint32_t rows = GetParam().second;
-    std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
-    std::string vec_type = "vec" + std::to_string(rows) + "<f32>";
-    std::string scalar_values;
-    std::string vector_values;
-    for (uint32_t c = 0; c < cols; c++) {
-        if (c > 0) {
-            vector_values += ", ";
-            scalar_values += ", ";
-        }
-        vector_values += vec_type + "(";
-        for (uint32_t r = 0; r < rows; r++) {
-            if (r > 0) {
-                scalar_values += ", ";
-                vector_values += ", ";
-            }
-            auto value = std::to_string(c * rows + r) + ".0";
-            scalar_values += value;
-            vector_values += value;
-        }
-        vector_values += ")";
-    }
-
-    std::string tmpl = R"(
-@fragment
-fn main() {
-  let m = ${matrix}(${values});
-}
-)";
-    tmpl = tint::ReplaceAll(tmpl, "${matrix}", mat_type);
-    auto src = tint::ReplaceAll(tmpl, "${values}", scalar_values);
-    auto expect = tint::ReplaceAll(tmpl, "${values}", vector_values);
-
-    EXPECT_TRUE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-
-    auto got = Run<VectorizeScalarMatrixInitializers>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(VectorizeScalarMatrixInitializersTest, MultipleScalarsViaReference) {
-    uint32_t cols = GetParam().first;
-    uint32_t rows = GetParam().second;
-    std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
-    std::string vec_type = "vec" + std::to_string(rows) + "<f32>";
-    std::string scalar_values;
-    std::string vector_values;
-    for (uint32_t c = 0; c < cols; c++) {
-        if (c > 0) {
-            vector_values += ", ";
-            scalar_values += ", ";
-        }
-        vector_values += vec_type + "(";
-        for (uint32_t r = 0; r < rows; r++) {
-            if (r > 0) {
-                scalar_values += ", ";
-                vector_values += ", ";
-            }
-            auto value = "v[" + std::to_string((c * rows + r) % 4) + "]";
-            scalar_values += value;
-            vector_values += value;
-        }
-        vector_values += ")";
-    }
-
-    std::string tmpl = R"(
-@fragment
-fn main() {
-  let v = vec4<f32>(1.0, 2.0, 3.0, 8.0);
-  let m = ${matrix}(${values});
-}
-)";
-    tmpl = tint::ReplaceAll(tmpl, "${matrix}", mat_type);
-    auto src = tint::ReplaceAll(tmpl, "${values}", scalar_values);
-    auto expect = tint::ReplaceAll(tmpl, "${values}", vector_values);
-
-    EXPECT_TRUE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-
-    auto got = Run<VectorizeScalarMatrixInitializers>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(VectorizeScalarMatrixInitializersTest, MultipleScalarsViaPointerIndex) {
-    uint32_t cols = GetParam().first;
-    uint32_t rows = GetParam().second;
-    std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
-    std::string vec_type = "vec" + std::to_string(rows) + "<f32>";
-    std::string scalar_values;
-    std::string vector_values;
-    for (uint32_t c = 0; c < cols; c++) {
-        if (c > 0) {
-            vector_values += ", ";
-            scalar_values += ", ";
-        }
-        vector_values += vec_type + "(";
-        for (uint32_t r = 0; r < rows; r++) {
-            if (r > 0) {
-                scalar_values += ", ";
-                vector_values += ", ";
-            }
-            auto value = "p[" + std::to_string((c * rows + r) % 4) + "]";
-            scalar_values += value;
-            vector_values += value;
-        }
-        vector_values += ")";
-    }
-
-    std::string tmpl = R"(
-@fragment
-fn main() {
-  var v = vec4<f32>(1.0, 2.0, 3.0, 8.0);
-  let p = &(v);
-  let m = ${matrix}(${values});
-}
-)";
-    tmpl = tint::ReplaceAll(tmpl, "${matrix}", mat_type);
-    auto src = tint::ReplaceAll(tmpl, "${values}", scalar_values);
-    auto expect = tint::ReplaceAll(tmpl, "${values}", vector_values);
-
-    EXPECT_TRUE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-
-    auto got = Run<VectorizeScalarMatrixInitializers>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_P(VectorizeScalarMatrixInitializersTest, NonScalarInitializers) {
-    uint32_t cols = GetParam().first;
-    uint32_t rows = GetParam().second;
-    std::string mat_type = "mat" + std::to_string(cols) + "x" + std::to_string(rows) + "<f32>";
-    std::string vec_type = "vec" + std::to_string(rows) + "<f32>";
-    std::string columns;
-    for (uint32_t c = 0; c < cols; c++) {
-        if (c > 0) {
-            columns += ", ";
-        }
-        columns += vec_type + "()";
-    }
-
-    std::string tmpl = R"(
-@fragment
-fn main() {
-  let m = ${matrix}(${columns});
-}
-)";
-    tmpl = tint::ReplaceAll(tmpl, "${matrix}", mat_type);
-    auto src = tint::ReplaceAll(tmpl, "${columns}", columns);
-    auto expect = src;
-
-    EXPECT_FALSE(ShouldRun<VectorizeScalarMatrixInitializers>(src));
-
-    auto got = Run<VectorizeScalarMatrixInitializers>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-INSTANTIATE_TEST_SUITE_P(VectorizeScalarMatrixInitializersTest,
-                         VectorizeScalarMatrixInitializersTest,
-                         testing::Values(std::make_pair(2, 2),
-                                         std::make_pair(2, 3),
-                                         std::make_pair(2, 4),
-                                         std::make_pair(3, 2),
-                                         std::make_pair(3, 3),
-                                         std::make_pair(3, 4),
-                                         std::make_pair(4, 2),
-                                         std::make_pair(4, 3),
-                                         std::make_pair(4, 4)));
-
-}  // namespace
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
deleted file mode 100644
index 0f08afa..0000000
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.cc
+++ /dev/null
@@ -1,522 +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/wgsl/ast/transform/zero_init_workgroup_memory.h"
-
-#include <algorithm>
-#include <map>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/core/builtin_value.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/atomic.h"
-#include "src/tint/lang/wgsl/ast/workgroup_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/function.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/containers/unique_vector.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ZeroInitWorkgroupMemory);
-
-namespace tint::ast::transform {
-namespace {
-
-bool ShouldRun(const Program& program) {
-    for (auto* global : program.AST().GlobalVariables()) {
-        if (auto* var = global->As<Var>()) {
-            auto* v = program.Sem().Get(var);
-            if (v->AddressSpace() == core::AddressSpace::kWorkgroup) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
-using StatementList = tint::Vector<const Statement*, 8>;
-
-/// PIMPL state for the transform
-struct ZeroInitWorkgroupMemory::State {
-    /// The clone context
-    program::CloneContext& ctx;
-
-    /// An alias to *ctx.dst
-    ast::Builder& b = *ctx.dst;
-
-    /// The semantic info for the source program.
-    const sem::Info& sem = ctx.src->Sem();
-
-    /// The constant size of the workgroup. If 0, then #workgroup_size_expr should
-    /// be used instead.
-    uint32_t workgroup_size_const = 0;
-    /// The size of the workgroup as an expression generator. Use if
-    /// #workgroup_size_const is 0.
-    std::function<const ast::Expression*()> workgroup_size_expr;
-
-    /// ArrayIndex represents a function on the local invocation index, of
-    /// the form: `array_index = (local_invocation_index % modulo) / division`
-    struct ArrayIndex {
-        /// The RHS of the modulus part of the expression
-        uint32_t modulo = 1;
-        /// The RHS of the division part of the expression
-        uint32_t division = 1;
-
-        /// @returns the hash code of the ArrayIndex
-        tint::HashCode HashCode() const { return Hash(modulo, division); }
-
-        /// Equality operator
-        /// @param i the ArrayIndex to compare to this ArrayIndex
-        /// @returns true if `i` and this ArrayIndex are equal
-        bool operator==(const ArrayIndex& i) const {
-            return modulo == i.modulo && division == i.division;
-        }
-    };
-
-    /// A list of unique ArrayIndex
-    using ArrayIndices = UniqueVector<ArrayIndex, 4>;
-
-    /// Expression holds information about an expression that is being built for a
-    /// statement will zero workgroup values.
-    struct Expression {
-        /// The AST expression node
-        const ast::Expression* expr = nullptr;
-        /// The number of iterations required to zero the value
-        uint32_t num_iterations = 0;
-        /// All array indices used by this expression
-        ArrayIndices array_indices;
-
-        /// @returns true if the expr is not null (null usually indicates a failure)
-        explicit operator bool() const { return expr != nullptr; }
-    };
-
-    /// Statement holds information about a statement that will zero workgroup
-    /// values.
-    struct Statement {
-        /// The AST statement node
-        const ast::Statement* stmt;
-        /// The number of iterations required to zero the value
-        uint32_t num_iterations;
-        /// All array indices used by this statement
-        ArrayIndices array_indices;
-    };
-
-    /// All statements that zero workgroup memory
-    std::vector<Statement> statements;
-
-    /// A map of ArrayIndex to the name reserved for the `let` declaration of that
-    /// index.
-    Hashmap<ArrayIndex, Symbol, 4> array_index_names;
-
-    /// Constructor
-    /// @param c the program::CloneContext used for the transform
-    explicit State(program::CloneContext& c) : ctx(c) {}
-
-    /// Run inserts the workgroup memory zero-initialization logic at the top of
-    /// the given function
-    /// @param fn a compute shader entry point function
-    void Run(const Function* fn) {
-        CalculateWorkgroupSize(GetAttribute<WorkgroupAttribute>(fn->attributes));
-
-        // Generate the workgroup zeroing function
-        auto zeroing_fn_name = BuildZeroingFn(fn);
-        if (!zeroing_fn_name) {
-            return;  // Nothing to do.
-        }
-
-        // Get or create the local invocation index parameter on the entry point
-        auto local_invocation_index = GetOrCreateLocalInvocationIndex(fn);
-
-        // Prefix the entry point body with a call to the workgroup zeroing function
-        ctx.InsertFront(fn->body->statements,
-                        b.CallStmt(b.Call(zeroing_fn_name, local_invocation_index)));
-    }
-
-    /// Builds a function that zeros all the variables in the workgroup address space transitively
-    /// used by @p fn. The built function takes a single `local_invocation_id : u32` parameter
-    /// @param fn the entry point function.
-    /// @return the name of the workgroup memory zeroing function.
-    Symbol BuildZeroingFn(const Function* fn) {
-        // Generate a list of statements to zero initialize each of the workgroup storage variables
-        // used by `fn`. This will populate #statements.
-        auto* func = sem.Get(fn);
-        for (auto* var : func->TransitivelyReferencedGlobals()) {
-            if (var->AddressSpace() == core::AddressSpace::kWorkgroup) {
-                auto get_expr = [&](uint32_t num_values) {
-                    auto var_name = ctx.Clone(var->Declaration()->name->symbol);
-                    return Expression{b.Expr(var_name), num_values, ArrayIndices{}};
-                };
-                if (!BuildZeroingStatements(var->Type()->UnwrapRef(), get_expr)) {
-                    return Symbol{};
-                }
-            }
-        }
-
-        if (statements.empty()) {
-            return Symbol{};  // No workgroup variables to initialize.
-        }
-
-        // Take the zeroing statements and bin them by the number of iterations
-        // required to zero the workgroup data. We then emit these in blocks,
-        // possibly wrapped in if-statements or for-loops.
-        std::unordered_map<uint32_t, std::vector<Statement>> stmts_by_num_iterations;
-        std::vector<uint32_t> num_sorted_iterations;
-        for (auto& s : statements) {
-            auto& stmts = stmts_by_num_iterations[s.num_iterations];
-            if (stmts.empty()) {
-                num_sorted_iterations.emplace_back(s.num_iterations);
-            }
-            stmts.emplace_back(s);
-        }
-        std::sort(num_sorted_iterations.begin(), num_sorted_iterations.end());
-
-        auto local_idx = b.Symbols().New("local_idx");
-
-        // Loop over the statements, grouped by num_iterations.
-        Vector<const ast::Statement*, 8> init_body;
-        for (auto num_iterations : num_sorted_iterations) {
-            auto& stmts = stmts_by_num_iterations[num_iterations];
-
-            // Gather all the array indices used by all the statements in the block.
-            ArrayIndices array_indices;
-            for (auto& s : stmts) {
-                for (auto& idx : s.array_indices) {
-                    array_indices.Add(idx);
-                }
-            }
-
-            // Determine the block type used to emit these statements.
-
-            // TODO(crbug.com/tint/2143): Always emit an if statement around zero init, even when
-            // workgroup size matches num_iteration, to work around bugs in certain drivers.
-            constexpr bool kWorkaroundUnconditionalZeroInitDriverBug = true;
-
-            if (workgroup_size_const == 0 || num_iterations > workgroup_size_const) {
-                // Either the workgroup size is dynamic, or smaller than num_iterations.
-                // In either case, we need to generate a for loop to ensure we
-                // initialize all the array elements.
-                //
-                //  for (var idx : u32 = local_index;
-                //           idx < num_iterations;
-                //           idx += workgroup_size) {
-                //    ...
-                //  }
-                auto idx = b.Symbols().New("idx");
-                auto* init = b.Decl(b.Var(idx, b.ty.u32(), b.Expr(local_idx)));
-                auto* cond = b.LessThan(idx, u32(num_iterations));
-                auto* cont = b.Assign(
-                    idx, b.Add(idx, workgroup_size_const ? b.Expr(u32(workgroup_size_const))
-                                                         : workgroup_size_expr()));
-
-                auto block =
-                    DeclareArrayIndices(num_iterations, array_indices, [&] { return b.Expr(idx); });
-                for (auto& s : stmts) {
-                    block.Push(s.stmt);
-                }
-                auto* for_loop = b.For(init, cond, cont, b.Block(block));
-                init_body.Push(for_loop);
-            } else if (num_iterations < workgroup_size_const ||
-                       kWorkaroundUnconditionalZeroInitDriverBug) {
-                // Workgroup size is a known constant, but is greater than
-                // num_iterations. Emit an if statement:
-                //
-                //  if (local_index < num_iterations) {
-                //    ...
-                //  }
-                auto* cond = b.LessThan(local_idx, u32(num_iterations));
-                auto block = DeclareArrayIndices(num_iterations, array_indices,
-                                                 [&] { return b.Expr(local_idx); });
-                for (auto& s : stmts) {
-                    block.Push(s.stmt);
-                }
-                auto* if_stmt = b.If(cond, b.Block(block));
-                init_body.Push(if_stmt);
-            } else {
-                // Workgroup size exactly equals num_iterations.
-                // No need for any conditionals. Just emit a basic block:
-                //
-                // {
-                //    ...
-                // }
-                auto block = DeclareArrayIndices(num_iterations, array_indices,
-                                                 [&] { return b.Expr(local_idx); });
-                for (auto& s : stmts) {
-                    block.Push(s.stmt);
-                }
-                init_body.Push(b.Block(std::move(block)));
-            }
-        }
-
-        // Append a single workgroup barrier after the zero initialization.
-        init_body.Push(b.CallStmt(b.Call("workgroupBarrier")));
-
-        // Generate the zero-init function.
-        auto name = b.Symbols().New("tint_zero_workgroup_memory");
-        b.Func(name, Vector{b.Param(local_idx, b.ty.u32())}, b.ty.void_(),
-               b.Block(std::move(init_body)));
-        return name;
-    }
-
-    /// Looks for an existing `local_invocation_index` parameter on the entry point function @p fn,
-    /// or adds a new parameter to the function if it doesn't exist.
-    /// @param fn the entry point function.
-    /// @return an expression to the `local_invocation_index` parameter.
-    const ast::Expression* GetOrCreateLocalInvocationIndex(const Function* fn) {
-        // Scan the entry point for an existing local_invocation_index builtin parameter
-        std::function<const ast::Expression*()> local_index;
-        for (auto* param : fn->params) {
-            if (auto* builtin_attr = GetAttribute<BuiltinAttribute>(param->attributes)) {
-                if (builtin_attr->builtin == core::BuiltinValue::kLocalInvocationIndex) {
-                    return b.Expr(ctx.Clone(param->name->symbol));
-                }
-            }
-
-            if (auto* str = sem.Get(param)->Type()->As<core::type::Struct>()) {
-                for (auto* member : str->Members()) {
-                    if (member->Attributes().builtin == core::BuiltinValue::kLocalInvocationIndex) {
-                        auto* param_expr = b.Expr(ctx.Clone(param->name->symbol));
-                        auto member_name = ctx.Clone(member->Name());
-                        return b.MemberAccessor(param_expr, member_name);
-                    }
-                }
-            }
-        }
-
-        // No existing local index parameter. Append one to the entry point.
-        auto param_name = b.Symbols().New("local_invocation_index");
-        auto* local_invocation_index = b.Builtin(core::BuiltinValue::kLocalInvocationIndex);
-        auto* param = b.Param(param_name, b.ty.u32(), tint::Vector{local_invocation_index});
-        ctx.InsertBack(fn->params, param);
-        return b.Expr(param->name->symbol);
-    }
-
-    /// BuildZeroingExpr is a function that builds a sub-expression used to zero
-    /// workgroup values. `num_values` is the number of elements that the
-    /// expression will be used to zero. Returns the expression.
-    using BuildZeroingExpr = std::function<Expression(uint32_t num_values)>;
-
-    /// BuildZeroingStatements() generates the statements required to zero
-    /// initialize the workgroup storage expression of type `ty`.
-    /// @param ty the expression type
-    /// @param get_expr a function that builds the AST nodes for the expression.
-    /// @returns true on success, false on failure
-    [[nodiscard]] bool BuildZeroingStatements(const core::type::Type* ty,
-                                              const BuildZeroingExpr& get_expr) {
-        if (CanTriviallyZero(ty)) {
-            auto var = get_expr(1u);
-            if (!var) {
-                return false;
-            }
-            auto* zero_init = b.Call(CreateASTTypeFor(ctx, ty));
-            statements.emplace_back(
-                Statement{b.Assign(var.expr, zero_init), var.num_iterations, var.array_indices});
-            return true;
-        }
-
-        if (auto* atomic = ty->As<core::type::Atomic>()) {
-            auto* zero_init = b.Call(CreateASTTypeFor(ctx, atomic->Type()));
-            auto expr = get_expr(1u);
-            if (!expr) {
-                return false;
-            }
-            auto* store = b.Call("atomicStore", b.AddressOf(expr.expr), zero_init);
-            statements.emplace_back(
-                Statement{b.CallStmt(store), expr.num_iterations, expr.array_indices});
-            return true;
-        }
-
-        if (auto* str = ty->As<core::type::Struct>()) {
-            for (auto* member : str->Members()) {
-                auto name = ctx.Clone(member->Name());
-                auto get_member = [&](uint32_t num_values) {
-                    auto s = get_expr(num_values);
-                    if (!s) {
-                        return Expression{};  // error
-                    }
-                    return Expression{b.MemberAccessor(s.expr, name), s.num_iterations,
-                                      s.array_indices};
-                };
-                if (!BuildZeroingStatements(member->Type(), get_member)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        if (auto* arr = ty->As<core::type::Array>()) {
-            auto get_el = [&](uint32_t num_values) {
-                // num_values is the number of values to zero for the element type.
-                // The number of iterations required to zero the array and its elements is:
-                //      `num_values * arr->Count()`
-                // The index for this array is:
-                //      `(idx % modulo) / division`
-                auto count = arr->ConstantCount();
-                if (!count) {
-                    ctx.dst->Diagnostics().AddError(Source{})
-                        << core::type::Array::kErrExpectedConstantCount;
-                    return Expression{};  // error
-                }
-                auto modulo = num_values * count.value();
-                auto division = num_values;
-                auto a = get_expr(modulo);
-                if (!a) {
-                    return Expression{};  // error
-                }
-                auto array_indices = a.array_indices;
-                array_indices.Add(ArrayIndex{modulo, division});
-                auto index = array_index_names.GetOrAdd(ArrayIndex{modulo, division},
-                                                        [&] { return b.Symbols().New("i"); });
-                return Expression{b.IndexAccessor(a.expr, index), a.num_iterations, array_indices};
-            };
-            return BuildZeroingStatements(arr->ElemType(), get_el);
-        }
-
-        TINT_UNREACHABLE() << "could not zero workgroup type: " << ty->FriendlyName();
-    }
-
-    /// DeclareArrayIndices returns a list of statements that contain the `let`
-    /// declarations for all of the ArrayIndices.
-    /// @param num_iterations the number of iterations for the block
-    /// @param array_indices the list of array indices to generate `let`
-    ///        declarations for
-    /// @param iteration a function that returns the index of the current
-    ///         iteration.
-    /// @returns the list of `let` statements that declare the array indices
-    StatementList DeclareArrayIndices(uint32_t num_iterations,
-                                      const ArrayIndices& array_indices,
-                                      const std::function<const ast::Expression*()>& iteration) {
-        StatementList stmts;
-        std::map<Symbol, ArrayIndex> indices_by_name;
-        for (auto index : array_indices) {
-            auto name = array_index_names.Get(index);
-            auto* mod = (num_iterations > index.modulo)
-                            ? b.create<BinaryExpression>(core::BinaryOp::kModulo, iteration(),
-                                                         b.Expr(u32(index.modulo)))
-                            : iteration();
-            auto* div = (index.division != 1u) ? b.Div(mod, u32(index.division)) : mod;
-            auto* decl = b.Decl(b.Let(*name, b.ty.u32(), div));
-            stmts.Push(decl);
-        }
-        return stmts;
-    }
-
-    /// CalculateWorkgroupSize initializes the members #workgroup_size_const and
-    /// #workgroup_size_expr with the linear workgroup size.
-    /// @param attr the workgroup attribute applied to the entry point function
-    void CalculateWorkgroupSize(const WorkgroupAttribute* attr) {
-        bool is_signed = false;
-        workgroup_size_const = 1u;
-        workgroup_size_expr = nullptr;
-        for (auto* expr : attr->Values()) {
-            if (!expr) {
-                continue;
-            }
-            if (auto* c = sem.GetVal(expr)->ConstantValue()) {
-                workgroup_size_const *= c->ValueAs<AInt>();
-                continue;
-            }
-            // Constant value could not be found. Build expression instead.
-            workgroup_size_expr = [this, expr, size = workgroup_size_expr] {
-                auto* e = ctx.Clone(expr);
-                if (ctx.src->TypeOf(expr)->UnwrapRef()->Is<core::type::I32>()) {
-                    e = b.Call<u32>(e);
-                }
-                return size ? b.Mul(size(), e) : e;
-            };
-        }
-        if (workgroup_size_expr) {
-            if (workgroup_size_const != 1) {
-                // Fold workgroup_size_const in to workgroup_size_expr
-                workgroup_size_expr = [this, is_signed, const_size = workgroup_size_const,
-                                       expr_size = workgroup_size_expr] {
-                    return is_signed ? b.Mul(expr_size(), i32(const_size))
-                                     : b.Mul(expr_size(), u32(const_size));
-                };
-            }
-            // Indicate that workgroup_size_expr should be used instead of the
-            // constant.
-            workgroup_size_const = 0;
-        }
-    }
-
-    /// @returns true if a variable with store type `ty` can be efficiently zeroed
-    /// by assignment of a value constructor without operands. If CanTriviallyZero() returns false,
-    /// then the type needs to be initialized by decomposing the initialization into multiple
-    /// sub-initializations.
-    /// @param ty the type to inspect
-    bool CanTriviallyZero(const core::type::Type* ty) {
-        if (ty->Is<core::type::Atomic>()) {
-            return false;
-        }
-        if (auto* str = ty->As<core::type::Struct>()) {
-            for (auto* member : str->Members()) {
-                if (!CanTriviallyZero(member->Type())) {
-                    return false;
-                }
-            }
-        }
-        if (ty->Is<core::type::Array>()) {
-            return false;
-        }
-        // True for all other storable types
-        return true;
-    }
-};
-
-ZeroInitWorkgroupMemory::ZeroInitWorkgroupMemory() = default;
-
-ZeroInitWorkgroupMemory::~ZeroInitWorkgroupMemory() = default;
-
-Transform::ApplyResult ZeroInitWorkgroupMemory::Apply(const Program& src,
-                                                      const DataMap&,
-                                                      DataMap&) const {
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    for (auto* fn : src.AST().Functions()) {
-        if (fn->PipelineStage() == PipelineStage::kCompute) {
-            State{ctx}.Run(fn);
-        }
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h
deleted file mode 100644
index aed3deb..0000000
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h
+++ /dev/null
@@ -1,57 +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_WGSL_AST_TRANSFORM_ZERO_INIT_WORKGROUP_MEMORY_H_
-#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_ZERO_INIT_WORKGROUP_MEMORY_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::ast::transform {
-
-/// ZeroInitWorkgroupMemory is a transform that injects code at the top of entry
-/// points to zero-initialize workgroup memory used by that entry point (and all
-/// transitive functions called by that entry point)
-class ZeroInitWorkgroupMemory final : public Castable<ZeroInitWorkgroupMemory, Transform> {
-  public:
-    /// Constructor
-    ZeroInitWorkgroupMemory();
-
-    /// Destructor
-    ~ZeroInitWorkgroupMemory() override;
-
-    /// @copydoc Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const DataMap& inputs,
-                      DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::ast::transform
-
-#endif  // SRC_TINT_LANG_WGSL_AST_TRANSFORM_ZERO_INIT_WORKGROUP_MEMORY_H_
diff --git a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_test.cc b/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_test.cc
deleted file mode 100644
index c5b8677..0000000
--- a/src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory_test.cc
+++ /dev/null
@@ -1,1552 +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/wgsl/ast/transform/zero_init_workgroup_memory.h"
-
-#include <utility>
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::ast::transform {
-namespace {
-
-using ZeroInitWorkgroupMemoryTest = TransformTest;
-
-TEST_F(ZeroInitWorkgroupMemoryTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<ZeroInitWorkgroupMemory>(src));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, ShouldRunHasNoWorkgroupVars) {
-    auto* src = R"(
-var<private> v : i32;
-)";
-
-    EXPECT_FALSE(ShouldRun<ZeroInitWorkgroupMemory>(src));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, ShouldRunHasWorkgroupVars) {
-    auto* src = R"(
-var<workgroup> a : i32;
-)";
-
-    EXPECT_TRUE(ShouldRun<ZeroInitWorkgroupMemory>(src));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = src;
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, NoWorkgroupVars) {
-    auto* src = R"(
-var<private> v : i32;
-
-fn f() {
-  v = 1;
-}
-)";
-    auto* expect = src;
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, UnreferencedWorkgroupVars) {
-    auto* src = R"(
-var<workgroup> a : i32;
-
-var<workgroup> b : i32;
-
-var<workgroup> c : i32;
-
-fn unreferenced() {
-  b = c;
-}
-
-@compute @workgroup_size(1)
-fn f() {
-}
-)";
-    auto* expect = src;
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, UnreferencedWorkgroupVars_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
-}
-
-fn unreferenced() {
-  b = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : i32;
-
-var<workgroup> c : i32;
-)";
-    auto* expect = src;
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndex) {
-    auto* src = R"(
-var<workgroup> v : i32;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  _ = v; // Initialization should be inserted above this statement
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    v = i32();
-  }
-  workgroupBarrier();
-}
-
-var<workgroup> v : i32;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  _ = v;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndex_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  _ = v; // Initialization should be inserted above this statement
-}
-
-var<workgroup> v : i32;
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    v = i32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  _ = v;
-}
-
-var<workgroup> v : i32;
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndexInStruct) {
-    auto* src = R"(
-var<workgroup> v : i32;
-
-struct Params {
-  @builtin(local_invocation_index) local_idx : u32,
-};
-
-@compute @workgroup_size(1)
-fn f(params : Params) {
-  _ = v; // Initialization should be inserted above this statement
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    v = i32();
-  }
-  workgroupBarrier();
-}
-
-var<workgroup> v : i32;
-
-struct Params {
-  @builtin(local_invocation_index)
-  local_idx : u32,
-}
-
-@compute @workgroup_size(1)
-fn f(params : Params) {
-  tint_zero_workgroup_memory(params.local_idx);
-  _ = v;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_ExistingLocalIndexInStruct_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f(params : Params) {
-  _ = v; // Initialization should be inserted above this statement
-}
-
-struct Params {
-  @builtin(local_invocation_index) local_idx : u32,
-};
-
-var<workgroup> v : i32;
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    v = i32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(params : Params) {
-  tint_zero_workgroup_memory(params.local_idx);
-  _ = v;
-}
-
-struct Params {
-  @builtin(local_invocation_index)
-  local_idx : u32,
-}
-
-var<workgroup> v : i32;
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_InjectedLocalIndex) {
-    auto* src = R"(
-var<workgroup> v : i32;
-
-@compute @workgroup_size(1)
-fn f() {
-  _ = v; // Initialization should be inserted above this statement
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    v = i32();
-  }
-  workgroupBarrier();
-}
-
-var<workgroup> v : i32;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = v;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, SingleWorkgroupVar_InjectedLocalIndex_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
-  _ = v; // Initialization should be inserted above this statement
-}
-
-var<workgroup> v : i32;
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    v = i32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = v;
-}
-
-var<workgroup> v : i32;
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size1) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  _ = a; // Initialization should be inserted above this statement
-  _ = b;
-  _ = c;
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    a = i32();
-    b.x = i32();
-  }
-  for(var idx : u32 = local_idx_1; (idx < 8u); idx = (idx + 1u)) {
-    let i : u32 = idx;
-    b.y[i] = i32();
-  }
-  for(var idx_1 : u32 = local_idx_1; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
-    let i_1 : u32 = idx_1;
-    c[i_1].x = i32();
-  }
-  for(var idx_2 : u32 = local_idx_1; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
-    let i_2 : u32 = (idx_2 / 8u);
-    let i : u32 = (idx_2 % 8u);
-    c[i_2].y[i] = i32();
-  }
-  workgroupBarrier();
-}
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  _ = a;
-  _ = b;
-  _ = c;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size1_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  _ = a; // Initialization should be inserted above this statement
-  _ = b;
-  _ = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-};
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    a = i32();
-    b.x = i32();
-  }
-  for(var idx : u32 = local_idx_1; (idx < 8u); idx = (idx + 1u)) {
-    let i : u32 = idx;
-    b.y[i] = i32();
-  }
-  for(var idx_1 : u32 = local_idx_1; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
-    let i_1 : u32 = idx_1;
-    c[i_1].x = i32();
-  }
-  for(var idx_2 : u32 = local_idx_1; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
-    let i_2 : u32 = (idx_2 / 8u);
-    let i : u32 = (idx_2 % 8u);
-    c[i_2].y[i] = i32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  _ = a;
-  _ = b;
-  _ = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size_2_3) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(2, 3)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  _ = a; // Initialization should be inserted above this statement
-  _ = b;
-  _ = c;
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    a = i32();
-    b.x = i32();
-  }
-  for(var idx : u32 = local_idx_1; (idx < 8u); idx = (idx + 6u)) {
-    let i : u32 = idx;
-    b.y[i] = i32();
-  }
-  for(var idx_1 : u32 = local_idx_1; (idx_1 < 32u); idx_1 = (idx_1 + 6u)) {
-    let i_1 : u32 = idx_1;
-    c[i_1].x = i32();
-  }
-  for(var idx_2 : u32 = local_idx_1; (idx_2 < 256u); idx_2 = (idx_2 + 6u)) {
-    let i_2 : u32 = (idx_2 / 8u);
-    let i : u32 = (idx_2 % 8u);
-    c[i_2].y[i] = i32();
-  }
-  workgroupBarrier();
-}
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(2, 3)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  _ = a;
-  _ = b;
-  _ = c;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size_2_3_X) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@id(1) override X : i32;
-
-@compute @workgroup_size(2, 3, X)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  _ = a; // Initialization should be inserted above this statement
-  _ = b;
-  _ = c;
-}
-)";
-    auto* expect =
-        R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  for(var idx : u32 = local_idx_1; (idx < 1u); idx = (idx + (u32(X) * 6u))) {
-    a = i32();
-    b.x = i32();
-  }
-  for(var idx_1 : u32 = local_idx_1; (idx_1 < 8u); idx_1 = (idx_1 + (u32(X) * 6u))) {
-    let i : u32 = idx_1;
-    b.y[i] = i32();
-  }
-  for(var idx_2 : u32 = local_idx_1; (idx_2 < 32u); idx_2 = (idx_2 + (u32(X) * 6u))) {
-    let i_1 : u32 = idx_2;
-    c[i_1].x = i32();
-  }
-  for(var idx_3 : u32 = local_idx_1; (idx_3 < 256u); idx_3 = (idx_3 + (u32(X) * 6u))) {
-    let i_2 : u32 = (idx_3 / 8u);
-    let i : u32 = (idx_3 % 8u);
-    c[i_2].y[i] = i32();
-  }
-  workgroupBarrier();
-}
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@id(1) override X : i32;
-
-@compute @workgroup_size(2, 3, X)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  _ = a;
-  _ = b;
-  _ = c;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_ExistingLocalIndex_Size_5u_X_10u) {
-    auto* src = R"(
-struct S {
-  x : array<array<i32, 8>, 10>,
-  y : array<i32, 8>,
-  z : array<array<array<i32, 8>, 10>, 20>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@id(1) override X : u32;
-
-@compute @workgroup_size(5u, X, 10u)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  _ = a; // Initialization should be inserted above this statement
-  _ = b;
-  _ = c;
-}
-)";
-    auto* expect =
-        R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  for(var idx : u32 = local_idx_1; (idx < 1u); idx = (idx + (X * 50u))) {
-    a = i32();
-  }
-  for(var idx_1 : u32 = local_idx_1; (idx_1 < 8u); idx_1 = (idx_1 + (X * 50u))) {
-    let i_1 : u32 = idx_1;
-    b.y[i_1] = i32();
-  }
-  for(var idx_2 : u32 = local_idx_1; (idx_2 < 80u); idx_2 = (idx_2 + (X * 50u))) {
-    let i : u32 = (idx_2 / 8u);
-    let i_1 : u32 = (idx_2 % 8u);
-    b.x[i][i_1] = i32();
-  }
-  for(var idx_3 : u32 = local_idx_1; (idx_3 < 256u); idx_3 = (idx_3 + (X * 50u))) {
-    let i_4 : u32 = (idx_3 / 8u);
-    let i_1 : u32 = (idx_3 % 8u);
-    c[i_4].y[i_1] = i32();
-  }
-  for(var idx_4 : u32 = local_idx_1; (idx_4 < 1600u); idx_4 = (idx_4 + (X * 50u))) {
-    let i_2 : u32 = (idx_4 / 80u);
-    let i : u32 = ((idx_4 % 80u) / 8u);
-    let i_1 : u32 = (idx_4 % 8u);
-    b.z[i_2][i][i_1] = i32();
-  }
-  for(var idx_5 : u32 = local_idx_1; (idx_5 < 2560u); idx_5 = (idx_5 + (X * 50u))) {
-    let i_3 : u32 = (idx_5 / 80u);
-    let i : u32 = ((idx_5 % 80u) / 8u);
-    let i_1 : u32 = (idx_5 % 8u);
-    c[i_3].x[i][i_1] = i32();
-  }
-  for(var idx_6 : u32 = local_idx_1; (idx_6 < 51200u); idx_6 = (idx_6 + (X * 50u))) {
-    let i_5 : u32 = (idx_6 / 1600u);
-    let i_2 : u32 = ((idx_6 % 1600u) / 80u);
-    let i : u32 = ((idx_6 % 80u) / 8u);
-    let i_1 : u32 = (idx_6 % 8u);
-    c[i_5].z[i_2][i][i_1] = i32();
-  }
-  workgroupBarrier();
-}
-
-struct S {
-  x : array<array<i32, 8>, 10>,
-  y : array<i32, 8>,
-  z : array<array<array<i32, 8>, 10>, 20>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@id(1) override X : u32;
-
-@compute @workgroup_size(5u, X, 10u)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  _ = a;
-  _ = b;
-  _ = c;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_InjectedLocalIndex) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
-  _ = a; // Initialization should be inserted above this statement
-  _ = b;
-  _ = c;
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    a = i32();
-    b.x = i32();
-  }
-  for(var idx : u32 = local_idx; (idx < 8u); idx = (idx + 1u)) {
-    let i : u32 = idx;
-    b.y[i] = i32();
-  }
-  for(var idx_1 : u32 = local_idx; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
-    let i_1 : u32 = idx_1;
-    c[i_1].x = i32();
-  }
-  for(var idx_2 : u32 = local_idx; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
-    let i_2 : u32 = (idx_2 / 8u);
-    let i : u32 = (idx_2 % 8u);
-    c[i_2].y[i] = i32();
-  }
-  workgroupBarrier();
-}
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = a;
-  _ = b;
-  _ = c;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_InjectedLocalIndex_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
-  _ = a; // Initialization should be inserted above this statement
-  _ = b;
-  _ = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-};
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    a = i32();
-    b.x = i32();
-  }
-  for(var idx : u32 = local_idx; (idx < 8u); idx = (idx + 1u)) {
-    let i : u32 = idx;
-    b.y[i] = i32();
-  }
-  for(var idx_1 : u32 = local_idx; (idx_1 < 32u); idx_1 = (idx_1 + 1u)) {
-    let i_1 : u32 = idx_1;
-    c[i_1].x = i32();
-  }
-  for(var idx_2 : u32 = local_idx; (idx_2 < 256u); idx_2 = (idx_2 + 1u)) {
-    let i_2 : u32 = (idx_2 / 8u);
-    let i : u32 = (idx_2 % 8u);
-    c[i_2].y[i] = i32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = a;
-  _ = b;
-  _ = c;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_MultipleEntryPoints) {
-    auto* src = R"(
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-};
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f1() {
-  _ = a; // Initialization should be inserted above this statement
-  _ = c;
-}
-
-@compute @workgroup_size(1, 2, 3)
-fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
-  _ = b; // Initialization should be inserted above this statement
-}
-
-@compute @workgroup_size(4, 5, 6)
-fn f3() {
-  _ = c; // Initialization should be inserted above this statement
-  _ = a;
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    a = i32();
-  }
-  for(var idx : u32 = local_idx; (idx < 32u); idx = (idx + 1u)) {
-    let i : u32 = idx;
-    c[i].x = i32();
-  }
-  for(var idx_1 : u32 = local_idx; (idx_1 < 256u); idx_1 = (idx_1 + 1u)) {
-    let i_1 : u32 = (idx_1 / 8u);
-    let i_2 : u32 = (idx_1 % 8u);
-    c[i_1].y[i_2] = i32();
-  }
-  workgroupBarrier();
-}
-
-fn tint_zero_workgroup_memory_1(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    b.x = i32();
-  }
-  for(var idx_2 : u32 = local_idx_1; (idx_2 < 8u); idx_2 = (idx_2 + 6u)) {
-    let i_3 : u32 = idx_2;
-    b.y[i_3] = i32();
-  }
-  workgroupBarrier();
-}
-
-fn tint_zero_workgroup_memory_2(local_idx_2 : u32) {
-  if ((local_idx_2 < 1u)) {
-    a = i32();
-  }
-  if ((local_idx_2 < 32u)) {
-    let i_4 : u32 = local_idx_2;
-    c[i_4].x = i32();
-  }
-  for(var idx_3 : u32 = local_idx_2; (idx_3 < 256u); idx_3 = (idx_3 + 120u)) {
-    let i_5 : u32 = (idx_3 / 8u);
-    let i_6 : u32 = (idx_3 % 8u);
-    c[i_5].y[i_6] = i32();
-  }
-  workgroupBarrier();
-}
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-@compute @workgroup_size(1)
-fn f1(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = a;
-  _ = c;
-}
-
-@compute @workgroup_size(1, 2, 3)
-fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index_1 : u32) {
-  tint_zero_workgroup_memory_1(local_invocation_index_1);
-  _ = b;
-}
-
-@compute @workgroup_size(4, 5, 6)
-fn f3(@builtin(local_invocation_index) local_invocation_index_2 : u32) {
-  tint_zero_workgroup_memory_2(local_invocation_index_2);
-  _ = c;
-  _ = a;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, MultipleWorkgroupVar_MultipleEntryPoints_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f1() {
-  _ = a; // Initialization should be inserted above this statement
-  _ = c;
-}
-
-@compute @workgroup_size(1, 2, 3)
-fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>) {
-  _ = b; // Initialization should be inserted above this statement
-}
-
-@compute @workgroup_size(4, 5, 6)
-fn f3() {
-  _ = c; // Initialization should be inserted above this statement
-  _ = a;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-};
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    a = i32();
-  }
-  for(var idx : u32 = local_idx; (idx < 32u); idx = (idx + 1u)) {
-    let i : u32 = idx;
-    c[i].x = i32();
-  }
-  for(var idx_1 : u32 = local_idx; (idx_1 < 256u); idx_1 = (idx_1 + 1u)) {
-    let i_1 : u32 = (idx_1 / 8u);
-    let i_2 : u32 = (idx_1 % 8u);
-    c[i_1].y[i_2] = i32();
-  }
-  workgroupBarrier();
-}
-
-fn tint_zero_workgroup_memory_1(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    b.x = i32();
-  }
-  for(var idx_2 : u32 = local_idx_1; (idx_2 < 8u); idx_2 = (idx_2 + 6u)) {
-    let i_3 : u32 = idx_2;
-    b.y[i_3] = i32();
-  }
-  workgroupBarrier();
-}
-
-fn tint_zero_workgroup_memory_2(local_idx_2 : u32) {
-  if ((local_idx_2 < 1u)) {
-    a = i32();
-  }
-  if ((local_idx_2 < 32u)) {
-    let i_4 : u32 = local_idx_2;
-    c[i_4].x = i32();
-  }
-  for(var idx_3 : u32 = local_idx_2; (idx_3 < 256u); idx_3 = (idx_3 + 120u)) {
-    let i_5 : u32 = (idx_3 / 8u);
-    let i_6 : u32 = (idx_3 % 8u);
-    c[i_5].y[i_6] = i32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f1(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = a;
-  _ = c;
-}
-
-@compute @workgroup_size(1, 2, 3)
-fn f2(@builtin(local_invocation_id) local_invocation_id : vec3<u32>, @builtin(local_invocation_index) local_invocation_index_1 : u32) {
-  tint_zero_workgroup_memory_1(local_invocation_index_1);
-  _ = b;
-}
-
-@compute @workgroup_size(4, 5, 6)
-fn f3(@builtin(local_invocation_index) local_invocation_index_2 : u32) {
-  tint_zero_workgroup_memory_2(local_invocation_index_2);
-  _ = c;
-  _ = a;
-}
-
-var<workgroup> a : i32;
-
-var<workgroup> b : S;
-
-var<workgroup> c : array<S, 32>;
-
-struct S {
-  x : i32,
-  y : array<i32, 8>,
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, TransitiveUsage) {
-    auto* src = R"(
-var<workgroup> v : i32;
-
-fn use_v() {
-  _ = v;
-}
-
-fn call_use_v() {
-  use_v();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  call_use_v(); // Initialization should be inserted above this statement
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    v = i32();
-  }
-  workgroupBarrier();
-}
-
-var<workgroup> v : i32;
-
-fn use_v() {
-  _ = v;
-}
-
-fn call_use_v() {
-  use_v();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  call_use_v();
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, TransitiveUsage_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  call_use_v(); // Initialization should be inserted above this statement
-}
-
-fn call_use_v() {
-  use_v();
-}
-
-fn use_v() {
-  _ = v;
-}
-
-var<workgroup> v : i32;
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx_1 : u32) {
-  if ((local_idx_1 < 1u)) {
-    v = i32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_idx : u32) {
-  tint_zero_workgroup_memory(local_idx);
-  call_use_v();
-}
-
-fn call_use_v() {
-  use_v();
-}
-
-fn use_v() {
-  _ = v;
-}
-
-var<workgroup> v : i32;
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupAtomics) {
-    auto* src = R"(
-var<workgroup> i : atomic<i32>;
-var<workgroup> u : atomic<u32>;
-
-@compute @workgroup_size(1)
-fn f() {
-  atomicLoad(&(i)); // Initialization should be inserted above this statement
-  atomicLoad(&(u));
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    atomicStore(&(i), i32());
-    atomicStore(&(u), u32());
-  }
-  workgroupBarrier();
-}
-
-var<workgroup> i : atomic<i32>;
-
-var<workgroup> u : atomic<u32>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  atomicLoad(&(i));
-  atomicLoad(&(u));
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupAtomics_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
-  atomicLoad(&(i)); // Initialization should be inserted above this statement
-  atomicLoad(&(u));
-}
-
-var<workgroup> i : atomic<i32>;
-var<workgroup> u : atomic<u32>;
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    atomicStore(&(i), i32());
-    atomicStore(&(u), u32());
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  atomicLoad(&(i));
-  atomicLoad(&(u));
-}
-
-var<workgroup> i : atomic<i32>;
-
-var<workgroup> u : atomic<u32>;
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupStructOfAtomics) {
-    auto* src = R"(
-struct S {
-  a : i32,
-  i : atomic<i32>,
-  b : f32,
-  u : atomic<u32>,
-  c : u32,
-};
-
-var<workgroup> w : S;
-
-@compute @workgroup_size(1)
-fn f() {
-  _ = w.a; // Initialization should be inserted above this statement
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    w.a = i32();
-    atomicStore(&(w.i), i32());
-    w.b = f32();
-    atomicStore(&(w.u), u32());
-    w.c = u32();
-  }
-  workgroupBarrier();
-}
-
-struct S {
-  a : i32,
-  i : atomic<i32>,
-  b : f32,
-  u : atomic<u32>,
-  c : u32,
-}
-
-var<workgroup> w : S;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = w.a;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupStructOfAtomics_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
-  _ = w.a; // Initialization should be inserted above this statement
-}
-
-var<workgroup> w : S;
-
-struct S {
-  a : i32,
-  i : atomic<i32>,
-  b : f32,
-  u : atomic<u32>,
-  c : u32,
-};
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    w.a = i32();
-    atomicStore(&(w.i), i32());
-    w.b = f32();
-    atomicStore(&(w.u), u32());
-    w.c = u32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = w.a;
-}
-
-var<workgroup> w : S;
-
-struct S {
-  a : i32,
-  i : atomic<i32>,
-  b : f32,
-  u : atomic<u32>,
-  c : u32,
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfAtomics) {
-    auto* src = R"(
-var<workgroup> w : array<atomic<u32>, 4>;
-
-@compute @workgroup_size(1)
-fn f() {
-  atomicLoad(&w[0]); // Initialization should be inserted above this statement
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  for(var idx : u32 = local_idx; (idx < 4u); idx = (idx + 1u)) {
-    let i : u32 = idx;
-    atomicStore(&(w[i]), u32());
-  }
-  workgroupBarrier();
-}
-
-var<workgroup> w : array<atomic<u32>, 4>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  atomicLoad(&(w[0]));
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfAtomics_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
-  atomicLoad(&w[0]); // Initialization should be inserted above this statement
-}
-
-var<workgroup> w : array<atomic<u32>, 4>;
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  for(var idx : u32 = local_idx; (idx < 4u); idx = (idx + 1u)) {
-    let i : u32 = idx;
-    atomicStore(&(w[i]), u32());
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  atomicLoad(&(w[0]));
-}
-
-var<workgroup> w : array<atomic<u32>, 4>;
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfStructOfAtomics) {
-    auto* src = R"(
-struct S {
-  a : i32,
-  i : atomic<i32>,
-  b : f32,
-  u : atomic<u32>,
-  c : u32,
-};
-
-var<workgroup> w : array<S, 4>;
-
-@compute @workgroup_size(1)
-fn f() {
-  _ = w[0].a; // Initialization should be inserted above this statement
-}
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  for(var idx : u32 = local_idx; (idx < 4u); idx = (idx + 1u)) {
-    let i_1 : u32 = idx;
-    w[i_1].a = i32();
-    atomicStore(&(w[i_1].i), i32());
-    w[i_1].b = f32();
-    atomicStore(&(w[i_1].u), u32());
-    w[i_1].c = u32();
-  }
-  workgroupBarrier();
-}
-
-struct S {
-  a : i32,
-  i : atomic<i32>,
-  b : f32,
-  u : atomic<u32>,
-  c : u32,
-}
-
-var<workgroup> w : array<S, 4>;
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = w[0].a;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, WorkgroupArrayOfStructOfAtomics_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn f() {
-  _ = w[0].a; // Initialization should be inserted above this statement
-}
-
-var<workgroup> w : array<S, 4>;
-
-struct S {
-  a : i32,
-  i : atomic<i32>,
-  b : f32,
-  u : atomic<u32>,
-  c : u32,
-};
-)";
-    auto* expect = R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  for(var idx : u32 = local_idx; (idx < 4u); idx = (idx + 1u)) {
-    let i_1 : u32 = idx;
-    w[i_1].a = i32();
-    atomicStore(&(w[i_1].i), i32());
-    w[i_1].b = f32();
-    atomicStore(&(w[i_1].u), u32());
-    w[i_1].c = u32();
-  }
-  workgroupBarrier();
-}
-
-@compute @workgroup_size(1)
-fn f(@builtin(local_invocation_index) local_invocation_index : u32) {
-  tint_zero_workgroup_memory(local_invocation_index);
-  _ = w[0].a;
-}
-
-var<workgroup> w : array<S, 4>;
-
-struct S {
-  a : i32,
-  i : atomic<i32>,
-  b : f32,
-  u : atomic<u32>,
-  c : u32,
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, ArrayWithOverrideCount) {
-    auto* src =
-        R"(override O = 123;
-alias A = array<i32, O*2>;
-
-var<workgroup> W : A;
-
-@compute @workgroup_size(1)
-fn main() {
-    let p : ptr<workgroup, A> = &W;
-    (*p)[0] = 42;
-}
-)";
-
-    auto* expect =
-        R"(error: array size is an override-expression, when expected a constant-expression.
-Was the SubstituteOverride transform run?)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(ZeroInitWorkgroupMemoryTest, AliasTypeWithParamName) {
-    auto* src =
-        R"(
-var<workgroup> W : mat2x2<f32>;
-
-@compute @workgroup_size(1) fn F(@builtin(local_invocation_index) mat2x2 : u32) {
-  W[0]+=0;
-}
-)";
-
-    auto* expect =
-        R"(
-fn tint_zero_workgroup_memory(local_idx : u32) {
-  if ((local_idx < 1u)) {
-    W = mat2x2<f32>();
-  }
-  workgroupBarrier();
-}
-
-var<workgroup> W : mat2x2<f32>;
-
-@compute @workgroup_size(1)
-fn F(@builtin(local_invocation_index) mat2x2 : u32) {
-  tint_zero_workgroup_memory(mat2x2);
-  W[0] += 0;
-}
-)";
-
-    auto got = Run<ZeroInitWorkgroupMemory>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::ast::transform