[hlsl] Remove the AST backend.

Removes the HLSL AST backend.

Bug: 421916945
Change-Id: I7dfe0128678099a9ab6161c870d5dbd4f29010eb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/248354
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/cmd/test/BUILD.bazel b/src/tint/cmd/test/BUILD.bazel
index facb4fe..c84d5ef 100644
--- a/src/tint/cmd/test/BUILD.bazel
+++ b/src/tint/cmd/test/BUILD.bazel
@@ -102,16 +102,10 @@
     "//conditions:default": [],
   }) + select({
     ":tint_build_hlsl_writer": [
-      "//src/tint/lang/hlsl/writer/ast_printer:test",
       "//src/tint/lang/hlsl/writer:test",
     ],
     "//conditions:default": [],
   }) + select({
-    ":tint_build_hlsl_writer_and_tint_build_wgsl_reader_and_tint_build_wgsl_writer": [
-      "//src/tint/lang/hlsl/writer/ast_raise:test",
-    ],
-    "//conditions:default": [],
-  }) + select({
     ":tint_build_ir_binary": [
       "//src/tint/lang/core/ir/binary:test",
     ],
@@ -247,14 +241,6 @@
     ],
 )
 selects.config_setting_group(
-    name = "tint_build_hlsl_writer_and_tint_build_wgsl_reader_and_tint_build_wgsl_writer",
-    match_all = [
-        ":tint_build_hlsl_writer",
-        ":tint_build_wgsl_reader",
-        ":tint_build_wgsl_writer",
-    ],
-)
-selects.config_setting_group(
     name = "tint_build_spv_reader_and_tint_build_wgsl_reader_and_tint_build_wgsl_writer",
     match_all = [
         ":tint_build_spv_reader",
diff --git a/src/tint/cmd/test/BUILD.cmake b/src/tint/cmd/test/BUILD.cmake
index 1365282..042cbdb 100644
--- a/src/tint/cmd/test/BUILD.cmake
+++ b/src/tint/cmd/test/BUILD.cmake
@@ -110,17 +110,10 @@
 
 if(TINT_BUILD_HLSL_WRITER)
   tint_target_add_dependencies(tint_cmd_test_test_cmd test_cmd
-    tint_lang_hlsl_writer_ast_printer_test
     tint_lang_hlsl_writer_test
   )
 endif(TINT_BUILD_HLSL_WRITER)
 
-if(TINT_BUILD_HLSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-  tint_target_add_dependencies(tint_cmd_test_test_cmd test_cmd
-    tint_lang_hlsl_writer_ast_raise_test
-  )
-endif(TINT_BUILD_HLSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-
 if(TINT_BUILD_IR_BINARY)
   tint_target_add_dependencies(tint_cmd_test_test_cmd test_cmd
     tint_lang_core_ir_binary_test
diff --git a/src/tint/cmd/test/BUILD.gn b/src/tint/cmd/test/BUILD.gn
index 64aff8a..9f9c343 100644
--- a/src/tint/cmd/test/BUILD.gn
+++ b/src/tint/cmd/test/BUILD.gn
@@ -109,15 +109,7 @@
     }
 
     if (tint_build_hlsl_writer) {
-      deps += [
-        "${tint_src_dir}/lang/hlsl/writer:unittests",
-        "${tint_src_dir}/lang/hlsl/writer/ast_printer:unittests",
-      ]
-    }
-
-    if (tint_build_hlsl_writer && tint_build_wgsl_reader &&
-        tint_build_wgsl_writer) {
-      deps += [ "${tint_src_dir}/lang/hlsl/writer/ast_raise:unittests" ]
+      deps += [ "${tint_src_dir}/lang/hlsl/writer:unittests" ]
     }
 
     if (tint_build_ir_binary) {
diff --git a/src/tint/lang/hlsl/writer/BUILD.bazel b/src/tint/lang/hlsl/writer/BUILD.bazel
index 0e84209..8f9f85b 100644
--- a/src/tint/lang/hlsl/writer/BUILD.bazel
+++ b/src/tint/lang/hlsl/writer/BUILD.bazel
@@ -53,11 +53,7 @@
     "//src/tint/lang/hlsl/writer/common",
     "//src/tint/lang/hlsl/writer/printer",
     "//src/tint/lang/hlsl/writer/raise",
-    "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast/transform",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/sem",
     "//src/tint/utils",
     "//src/tint/utils/containers",
     "//src/tint/utils/diagnostic",
@@ -68,15 +64,8 @@
     "//src/tint/utils/rtti",
     "//src/tint/utils/symbol",
     "//src/tint/utils/text",
-    "//src/tint/utils/text_generator",
     "//src/utils",
-  ] + select({
-    ":tint_build_hlsl_writer": [
-      "//src/tint/lang/hlsl/writer/ast_printer",
-      "//src/tint/lang/hlsl/writer/ast_raise",
-    ],
-    "//conditions:default": [],
-  }),
+  ],
   copts = COPTS,
   visibility = ["//visibility:public"],
 )
@@ -139,8 +128,3 @@
   actual = "//src/tint:tint_build_hlsl_writer_true",
 )
 
-alias(
-  name = "tint_build_wgsl_reader",
-  actual = "//src/tint:tint_build_wgsl_reader_true",
-)
-
diff --git a/src/tint/lang/hlsl/writer/BUILD.cmake b/src/tint/lang/hlsl/writer/BUILD.cmake
index 6ee58ca..b0766e7 100644
--- a/src/tint/lang/hlsl/writer/BUILD.cmake
+++ b/src/tint/lang/hlsl/writer/BUILD.cmake
@@ -34,8 +34,6 @@
 #                       Do not modify this file directly
 ################################################################################
 
-include(lang/hlsl/writer/ast_printer/BUILD.cmake)
-include(lang/hlsl/writer/ast_raise/BUILD.cmake)
 include(lang/hlsl/writer/common/BUILD.cmake)
 include(lang/hlsl/writer/helpers/BUILD.cmake)
 include(lang/hlsl/writer/printer/BUILD.cmake)
@@ -61,11 +59,7 @@
   tint_lang_hlsl_writer_common
   tint_lang_hlsl_writer_printer
   tint_lang_hlsl_writer_raise
-  tint_lang_wgsl
   tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_program
-  tint_lang_wgsl_sem
   tint_utils
   tint_utils_containers
   tint_utils_diagnostic
@@ -76,20 +70,12 @@
   tint_utils_rtti
   tint_utils_symbol
   tint_utils_text
-  tint_utils_text_generator
 )
 
 tint_target_add_external_dependencies(tint_lang_hlsl_writer lib
   "src_utils"
 )
 
-if(TINT_BUILD_HLSL_WRITER)
-  tint_target_add_dependencies(tint_lang_hlsl_writer lib
-    tint_lang_hlsl_writer_ast_printer
-    tint_lang_hlsl_writer_ast_raise
-  )
-endif(TINT_BUILD_HLSL_WRITER)
-
 endif(TINT_BUILD_HLSL_WRITER)
 if(TINT_BUILD_HLSL_WRITER)
 ################################################################################
@@ -170,11 +156,7 @@
   tint_lang_core_type
   tint_lang_hlsl_writer_common
   tint_lang_hlsl_writer_helpers
-  tint_lang_wgsl
   tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_program
-  tint_lang_wgsl_sem
   tint_utils
   tint_utils_bytes
   tint_utils_command
@@ -200,13 +182,4 @@
   )
 endif(TINT_BUILD_HLSL_WRITER)
 
-if(TINT_BUILD_WGSL_READER)
-  tint_target_add_sources(tint_lang_hlsl_writer_fuzz fuzz
-    "lang/hlsl/writer/writer_ast_fuzz.cc"
-  )
-  tint_target_add_dependencies(tint_lang_hlsl_writer_fuzz fuzz
-    tint_cmd_fuzz_wgsl_fuzz
-  )
-endif(TINT_BUILD_WGSL_READER)
-
 endif(TINT_BUILD_HLSL_WRITER)
\ No newline at end of file
diff --git a/src/tint/lang/hlsl/writer/BUILD.gn b/src/tint/lang/hlsl/writer/BUILD.gn
index b2a8e49..328b2ea 100644
--- a/src/tint/lang/hlsl/writer/BUILD.gn
+++ b/src/tint/lang/hlsl/writer/BUILD.gn
@@ -58,11 +58,7 @@
       "${tint_src_dir}/lang/hlsl/writer/common",
       "${tint_src_dir}/lang/hlsl/writer/printer",
       "${tint_src_dir}/lang/hlsl/writer/raise",
-      "${tint_src_dir}/lang/wgsl",
       "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/ast/transform",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/sem",
       "${tint_src_dir}/utils",
       "${tint_src_dir}/utils/containers",
       "${tint_src_dir}/utils/diagnostic",
@@ -73,15 +69,7 @@
       "${tint_src_dir}/utils/rtti",
       "${tint_src_dir}/utils/symbol",
       "${tint_src_dir}/utils/text",
-      "${tint_src_dir}/utils/text_generator",
     ]
-
-    if (tint_build_hlsl_writer) {
-      deps += [
-        "${tint_src_dir}/lang/hlsl/writer/ast_printer",
-        "${tint_src_dir}/lang/hlsl/writer/ast_raise",
-      ]
-    }
   }
 }
 if (tint_build_unittests) {
@@ -151,11 +139,7 @@
       "${tint_src_dir}/lang/core/type",
       "${tint_src_dir}/lang/hlsl/writer/common",
       "${tint_src_dir}/lang/hlsl/writer/helpers",
-      "${tint_src_dir}/lang/wgsl",
       "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/ast/transform",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/sem",
       "${tint_src_dir}/utils",
       "${tint_src_dir}/utils/bytes",
       "${tint_src_dir}/utils/command",
@@ -176,10 +160,5 @@
         "${tint_src_dir}/lang/hlsl/writer",
       ]
     }
-
-    if (tint_build_wgsl_reader) {
-      sources += [ "writer_ast_fuzz.cc" ]
-      deps += [ "${tint_src_dir}/cmd/fuzz/wgsl:fuzz" ]
-    }
   }
 }
diff --git a/src/tint/lang/hlsl/writer/ast_printer/BUILD.bazel b/src/tint/lang/hlsl/writer/ast_printer/BUILD.bazel
deleted file mode 100644
index 7da657b..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/BUILD.bazel
+++ /dev/null
@@ -1,159 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.bazel.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-load("//src/tint:flags.bzl", "COPTS")
-load("@bazel_skylib//lib:selects.bzl", "selects")
-cc_library(
-  name = "ast_printer",
-  srcs = [
-    "ast_printer.cc",
-  ],
-  hdrs = [
-    "ast_printer.h",
-  ],
-  deps = [
-    "//src/tint/api/common",
-    "//src/tint/lang/core",
-    "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/ir/transform",
-    "//src/tint/lang/core/type",
-    "//src/tint/lang/hlsl/writer/common",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast/transform",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/sem",
-    "//src/tint/utils",
-    "//src/tint/utils/containers",
-    "//src/tint/utils/diagnostic",
-    "//src/tint/utils/ice",
-    "//src/tint/utils/macros",
-    "//src/tint/utils/math",
-    "//src/tint/utils/memory",
-    "//src/tint/utils/rtti",
-    "//src/tint/utils/strconv",
-    "//src/tint/utils/symbol",
-    "//src/tint/utils/text",
-    "//src/tint/utils/text_generator",
-    "//src/utils",
-  ] + select({
-    ":tint_build_hlsl_writer": [
-      "//src/tint/lang/hlsl/writer/ast_raise",
-    ],
-    "//conditions:default": [],
-  }),
-  copts = COPTS,
-  visibility = ["//visibility:public"],
-)
-cc_library(
-  name = "test",
-  alwayslink = True,
-  srcs = [
-    "array_accessor_test.cc",
-    "assign_test.cc",
-    "ast_printer_test.cc",
-    "binary_test.cc",
-    "bitcast_test.cc",
-    "block_test.cc",
-    "break_test.cc",
-    "builtin_test.cc",
-    "builtin_texture_test.cc",
-    "call_test.cc",
-    "case_test.cc",
-    "cast_test.cc",
-    "const_assert_test.cc",
-    "constructor_test.cc",
-    "continue_test.cc",
-    "discard_test.cc",
-    "function_test.cc",
-    "helper_test.h",
-    "identifier_test.cc",
-    "if_test.cc",
-    "import_test.cc",
-    "loop_test.cc",
-    "member_accessor_test.cc",
-    "module_constant_test.cc",
-    "return_test.cc",
-    "sanitizer_test.cc",
-    "switch_test.cc",
-    "type_test.cc",
-    "unary_op_test.cc",
-    "variable_decl_statement_test.cc",
-    "workgroup_var_test.cc",
-  ],
-  deps = [
-    "//src/tint/api/common",
-    "//src/tint/lang/core",
-    "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/type",
-    "//src/tint/lang/hlsl/writer/common",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast/transform",
-    "//src/tint/lang/wgsl/ast:test",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/resolver",
-    "//src/tint/lang/wgsl/sem",
-    "//src/tint/utils",
-    "//src/tint/utils/containers",
-    "//src/tint/utils/diagnostic",
-    "//src/tint/utils/ice",
-    "//src/tint/utils/macros",
-    "//src/tint/utils/math",
-    "//src/tint/utils/memory",
-    "//src/tint/utils/rtti",
-    "//src/tint/utils/symbol",
-    "//src/tint/utils/text",
-    "//src/tint/utils/text_generator",
-    "@gtest",
-    "//src/utils",
-  ] + select({
-    ":tint_build_hlsl_writer": [
-      "//src/tint/lang/hlsl/writer",
-      "//src/tint/lang/hlsl/writer/ast_printer",
-      "//src/tint/lang/hlsl/writer/ast_raise",
-    ],
-    "//conditions:default": [],
-  }),
-  copts = COPTS,
-  visibility = ["//visibility:public"],
-)
-
-alias(
-  name = "tint_build_hlsl_writer",
-  actual = "//src/tint:tint_build_hlsl_writer_true",
-)
-
diff --git a/src/tint/lang/hlsl/writer/ast_printer/BUILD.cfg b/src/tint/lang/hlsl/writer/ast_printer/BUILD.cfg
deleted file mode 100644
index 31b4636..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/BUILD.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "condition": "tint_build_hlsl_writer"
-}
diff --git a/src/tint/lang/hlsl/writer/ast_printer/BUILD.cmake b/src/tint/lang/hlsl/writer/ast_printer/BUILD.cmake
deleted file mode 100644
index 4b09f81..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/BUILD.cmake
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.cmake.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-if(TINT_BUILD_HLSL_WRITER)
-################################################################################
-# Target:    tint_lang_hlsl_writer_ast_printer
-# Kind:      lib
-# Condition: TINT_BUILD_HLSL_WRITER
-################################################################################
-tint_add_target(tint_lang_hlsl_writer_ast_printer lib
-  lang/hlsl/writer/ast_printer/ast_printer.cc
-  lang/hlsl/writer/ast_printer/ast_printer.h
-)
-
-tint_target_add_dependencies(tint_lang_hlsl_writer_ast_printer lib
-  tint_api_common
-  tint_lang_core
-  tint_lang_core_constant
-  tint_lang_core_ir_transform
-  tint_lang_core_type
-  tint_lang_hlsl_writer_common
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_program
-  tint_lang_wgsl_sem
-  tint_utils
-  tint_utils_containers
-  tint_utils_diagnostic
-  tint_utils_ice
-  tint_utils_macros
-  tint_utils_math
-  tint_utils_memory
-  tint_utils_rtti
-  tint_utils_strconv
-  tint_utils_symbol
-  tint_utils_text
-  tint_utils_text_generator
-)
-
-tint_target_add_external_dependencies(tint_lang_hlsl_writer_ast_printer lib
-  "src_utils"
-)
-
-if(TINT_BUILD_HLSL_WRITER)
-  tint_target_add_dependencies(tint_lang_hlsl_writer_ast_printer lib
-    tint_lang_hlsl_writer_ast_raise
-  )
-endif(TINT_BUILD_HLSL_WRITER)
-
-endif(TINT_BUILD_HLSL_WRITER)
-if(TINT_BUILD_HLSL_WRITER)
-################################################################################
-# Target:    tint_lang_hlsl_writer_ast_printer_test
-# Kind:      test
-# Condition: TINT_BUILD_HLSL_WRITER
-################################################################################
-tint_add_target(tint_lang_hlsl_writer_ast_printer_test test
-  lang/hlsl/writer/ast_printer/array_accessor_test.cc
-  lang/hlsl/writer/ast_printer/assign_test.cc
-  lang/hlsl/writer/ast_printer/ast_printer_test.cc
-  lang/hlsl/writer/ast_printer/binary_test.cc
-  lang/hlsl/writer/ast_printer/bitcast_test.cc
-  lang/hlsl/writer/ast_printer/block_test.cc
-  lang/hlsl/writer/ast_printer/break_test.cc
-  lang/hlsl/writer/ast_printer/builtin_test.cc
-  lang/hlsl/writer/ast_printer/builtin_texture_test.cc
-  lang/hlsl/writer/ast_printer/call_test.cc
-  lang/hlsl/writer/ast_printer/case_test.cc
-  lang/hlsl/writer/ast_printer/cast_test.cc
-  lang/hlsl/writer/ast_printer/const_assert_test.cc
-  lang/hlsl/writer/ast_printer/constructor_test.cc
-  lang/hlsl/writer/ast_printer/continue_test.cc
-  lang/hlsl/writer/ast_printer/discard_test.cc
-  lang/hlsl/writer/ast_printer/function_test.cc
-  lang/hlsl/writer/ast_printer/helper_test.h
-  lang/hlsl/writer/ast_printer/identifier_test.cc
-  lang/hlsl/writer/ast_printer/if_test.cc
-  lang/hlsl/writer/ast_printer/import_test.cc
-  lang/hlsl/writer/ast_printer/loop_test.cc
-  lang/hlsl/writer/ast_printer/member_accessor_test.cc
-  lang/hlsl/writer/ast_printer/module_constant_test.cc
-  lang/hlsl/writer/ast_printer/return_test.cc
-  lang/hlsl/writer/ast_printer/sanitizer_test.cc
-  lang/hlsl/writer/ast_printer/switch_test.cc
-  lang/hlsl/writer/ast_printer/type_test.cc
-  lang/hlsl/writer/ast_printer/unary_op_test.cc
-  lang/hlsl/writer/ast_printer/variable_decl_statement_test.cc
-  lang/hlsl/writer/ast_printer/workgroup_var_test.cc
-)
-
-tint_target_add_dependencies(tint_lang_hlsl_writer_ast_printer_test test
-  tint_api_common
-  tint_lang_core
-  tint_lang_core_constant
-  tint_lang_core_type
-  tint_lang_hlsl_writer_common
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_ast_test
-  tint_lang_wgsl_program
-  tint_lang_wgsl_resolver
-  tint_lang_wgsl_sem
-  tint_utils
-  tint_utils_containers
-  tint_utils_diagnostic
-  tint_utils_ice
-  tint_utils_macros
-  tint_utils_math
-  tint_utils_memory
-  tint_utils_rtti
-  tint_utils_symbol
-  tint_utils_text
-  tint_utils_text_generator
-)
-
-tint_target_add_external_dependencies(tint_lang_hlsl_writer_ast_printer_test test
-  "gtest"
-  "src_utils"
-)
-
-if(TINT_BUILD_HLSL_WRITER)
-  tint_target_add_dependencies(tint_lang_hlsl_writer_ast_printer_test test
-    tint_lang_hlsl_writer
-    tint_lang_hlsl_writer_ast_printer
-    tint_lang_hlsl_writer_ast_raise
-  )
-endif(TINT_BUILD_HLSL_WRITER)
-
-endif(TINT_BUILD_HLSL_WRITER)
\ No newline at end of file
diff --git a/src/tint/lang/hlsl/writer/ast_printer/BUILD.gn b/src/tint/lang/hlsl/writer/ast_printer/BUILD.gn
deleted file mode 100644
index f637dfd..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/BUILD.gn
+++ /dev/null
@@ -1,156 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.gn.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-import("../../../../../../scripts/dawn_overrides_with_defaults.gni")
-import("../../../../../../scripts/tint_overrides_with_defaults.gni")
-
-import("${tint_src_dir}/tint.gni")
-
-if (tint_build_unittests || tint_build_benchmarks) {
-  import("//testing/test.gni")
-}
-if (tint_build_hlsl_writer) {
-  libtint_source_set("ast_printer") {
-    sources = [
-      "ast_printer.cc",
-      "ast_printer.h",
-    ]
-    deps = [
-      "${dawn_root}/src/utils:utils",
-      "${tint_src_dir}/api/common",
-      "${tint_src_dir}/lang/core",
-      "${tint_src_dir}/lang/core/constant",
-      "${tint_src_dir}/lang/core/ir/transform",
-      "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/hlsl/writer/common",
-      "${tint_src_dir}/lang/wgsl",
-      "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/ast/transform",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/sem",
-      "${tint_src_dir}/utils",
-      "${tint_src_dir}/utils/containers",
-      "${tint_src_dir}/utils/diagnostic",
-      "${tint_src_dir}/utils/ice",
-      "${tint_src_dir}/utils/macros",
-      "${tint_src_dir}/utils/math",
-      "${tint_src_dir}/utils/memory",
-      "${tint_src_dir}/utils/rtti",
-      "${tint_src_dir}/utils/strconv",
-      "${tint_src_dir}/utils/symbol",
-      "${tint_src_dir}/utils/text",
-      "${tint_src_dir}/utils/text_generator",
-    ]
-
-    if (tint_build_hlsl_writer) {
-      deps += [ "${tint_src_dir}/lang/hlsl/writer/ast_raise" ]
-    }
-  }
-}
-if (tint_build_unittests) {
-  if (tint_build_hlsl_writer) {
-    tint_unittests_source_set("unittests") {
-      sources = [
-        "array_accessor_test.cc",
-        "assign_test.cc",
-        "ast_printer_test.cc",
-        "binary_test.cc",
-        "bitcast_test.cc",
-        "block_test.cc",
-        "break_test.cc",
-        "builtin_test.cc",
-        "builtin_texture_test.cc",
-        "call_test.cc",
-        "case_test.cc",
-        "cast_test.cc",
-        "const_assert_test.cc",
-        "constructor_test.cc",
-        "continue_test.cc",
-        "discard_test.cc",
-        "function_test.cc",
-        "helper_test.h",
-        "identifier_test.cc",
-        "if_test.cc",
-        "import_test.cc",
-        "loop_test.cc",
-        "member_accessor_test.cc",
-        "module_constant_test.cc",
-        "return_test.cc",
-        "sanitizer_test.cc",
-        "switch_test.cc",
-        "type_test.cc",
-        "unary_op_test.cc",
-        "variable_decl_statement_test.cc",
-        "workgroup_var_test.cc",
-      ]
-      deps = [
-        "${dawn_root}/src/utils:utils",
-        "${tint_src_dir}:gmock_and_gtest",
-        "${tint_src_dir}/api/common",
-        "${tint_src_dir}/lang/core",
-        "${tint_src_dir}/lang/core/constant",
-        "${tint_src_dir}/lang/core/type",
-        "${tint_src_dir}/lang/hlsl/writer/common",
-        "${tint_src_dir}/lang/wgsl",
-        "${tint_src_dir}/lang/wgsl/ast",
-        "${tint_src_dir}/lang/wgsl/ast:unittests",
-        "${tint_src_dir}/lang/wgsl/ast/transform",
-        "${tint_src_dir}/lang/wgsl/program",
-        "${tint_src_dir}/lang/wgsl/resolver",
-        "${tint_src_dir}/lang/wgsl/sem",
-        "${tint_src_dir}/utils",
-        "${tint_src_dir}/utils/containers",
-        "${tint_src_dir}/utils/diagnostic",
-        "${tint_src_dir}/utils/ice",
-        "${tint_src_dir}/utils/macros",
-        "${tint_src_dir}/utils/math",
-        "${tint_src_dir}/utils/memory",
-        "${tint_src_dir}/utils/rtti",
-        "${tint_src_dir}/utils/symbol",
-        "${tint_src_dir}/utils/text",
-        "${tint_src_dir}/utils/text_generator",
-      ]
-
-      if (tint_build_hlsl_writer) {
-        deps += [
-          "${tint_src_dir}/lang/hlsl/writer",
-          "${tint_src_dir}/lang/hlsl/writer/ast_printer",
-          "${tint_src_dir}/lang/hlsl/writer/ast_raise",
-        ]
-      }
-    }
-  }
-}
diff --git a/src/tint/lang/hlsl/writer/ast_printer/array_accessor_test.cc b/src/tint/lang/hlsl/writer/ast_printer/array_accessor_test.cc
deleted file mode 100644
index fb7626d..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/array_accessor_test.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Expression = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Expression, IndexAccessor) {
-    GlobalVar("ary", ty.array<i32, 10>(), core::AddressSpace::kPrivate);
-    auto* expr = IndexAccessor("ary", 5_i);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "ary[5]");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/assign_test.cc b/src/tint/lang/hlsl/writer/ast_printer/assign_test.cc
deleted file mode 100644
index f2734ca..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/assign_test.cc
+++ /dev/null
@@ -1,438 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Assign = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Assign) {
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.i32())),
-             Decl(Var("rhs", ty.i32())),
-             Assign("lhs", "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void fn() {
-  int lhs = 0;
-  int rhs = 0;
-  lhs = rhs;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Vector_Assign_LetIndex) {
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.vec3<f32>())),
-             Decl(Var("rhs", ty.f32())),
-             Decl(Let("index", ty.u32(), Expr(0_u))),
-             Assign(IndexAccessor("lhs", "index"), "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void set_vector_element(inout float3 vec, int idx, float val) {
-  vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;
-}
-
-void fn() {
-  float3 lhs = float3(0.0f, 0.0f, 0.0f);
-  float rhs = 0.0f;
-  uint index = 0u;
-  set_vector_element(lhs, index, rhs);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Vector_Assign_ConstIndex) {
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.vec3<f32>())),
-             Decl(Var("rhs", ty.f32())),
-             Decl(Const("index", ty.u32(), Expr(0_u))),
-             Assign(IndexAccessor("lhs", "index"), "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void fn() {
-  float3 lhs = float3(0.0f, 0.0f, 0.0f);
-  float rhs = 0.0f;
-  lhs[0u] = rhs;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Vector_Assign_DynamicIndex) {
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.vec3<f32>())),
-             Decl(Var("rhs", ty.f32())),
-             Decl(Var("index", ty.u32())),
-             Assign(IndexAccessor("lhs", "index"), "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void set_vector_element(inout float3 vec, int idx, float val) {
-  vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;
-}
-
-void fn() {
-  float3 lhs = float3(0.0f, 0.0f, 0.0f);
-  float rhs = 0.0f;
-  uint index = 0u;
-  set_vector_element(lhs, index, rhs);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Matrix_Assign_Vector_LetIndex) {
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.mat4x2<f32>())),
-             Decl(Var("rhs", ty.vec2<f32>())),
-             Decl(Let("index", ty.u32(), Expr(0_u))),
-             Assign(IndexAccessor("lhs", "index"), "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void set_matrix_column(inout float4x2 mat, int col, float2 val) {
-  switch (col) {
-    case 0: mat[0] = val; break;
-    case 1: mat[1] = val; break;
-    case 2: mat[2] = val; break;
-    case 3: mat[3] = val; break;
-  }
-}
-
-void fn() {
-  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
-  float2 rhs = float2(0.0f, 0.0f);
-  uint index = 0u;
-  set_matrix_column(lhs, index, rhs);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Matrix_Assign_Vector_ConstIndex) {
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.mat4x2<f32>())),
-             Decl(Var("rhs", ty.vec2<f32>())),
-             Decl(Const("index", ty.u32(), Expr(0_u))),
-             Assign(IndexAccessor("lhs", "index"), "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void fn() {
-  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
-  float2 rhs = float2(0.0f, 0.0f);
-  lhs[0u] = rhs;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Matrix_Assign_Vector_DynamicIndex) {
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.mat4x2<f32>())),
-             Decl(Var("rhs", ty.vec2<f32>())),
-             Decl(Var("index", ty.u32())),
-             Assign(IndexAccessor("lhs", "index"), "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void set_matrix_column(inout float4x2 mat, int col, float2 val) {
-  switch (col) {
-    case 0: mat[0] = val; break;
-    case 1: mat[1] = val; break;
-    case 2: mat[2] = val; break;
-    case 3: mat[3] = val; break;
-  }
-}
-
-void fn() {
-  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
-  float2 rhs = float2(0.0f, 0.0f);
-  uint index = 0u;
-  set_matrix_column(lhs, index, rhs);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Matrix_Assign_Scalar_LetIndices) {
-    auto* col = IndexAccessor("lhs", "col");
-    auto* el = IndexAccessor(col, "row");
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.mat4x2<f32>())),
-             Decl(Var("rhs", ty.f32())),
-             Decl(Let("col", ty.u32(), Expr(0_u))),
-             Decl(Let("row", ty.u32(), Expr(1_u))),
-             Assign(el, "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void set_matrix_scalar(inout float4x2 mat, int col, int row, float val) {
-  switch (col) {
-    case 0:
-      mat[0] = (row.xx == int2(0, 1)) ? val.xx : mat[0];
-      break;
-    case 1:
-      mat[1] = (row.xx == int2(0, 1)) ? val.xx : mat[1];
-      break;
-    case 2:
-      mat[2] = (row.xx == int2(0, 1)) ? val.xx : mat[2];
-      break;
-    case 3:
-      mat[3] = (row.xx == int2(0, 1)) ? val.xx : mat[3];
-      break;
-  }
-}
-
-void fn() {
-  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
-  float rhs = 0.0f;
-  uint col = 0u;
-  uint row = 1u;
-  set_matrix_scalar(lhs, col, row, rhs);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Matrix_Assign_Scalar_ConstIndices) {
-    auto* col = IndexAccessor("lhs", "col");
-    auto* el = IndexAccessor(col, "row");
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.mat4x2<f32>())),
-             Decl(Var("rhs", ty.f32())),
-             Decl(Const("col", ty.u32(), Expr(0_u))),
-             Decl(Const("row", ty.u32(), Expr(1_u))),
-             Assign(el, "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void fn() {
-  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
-  float rhs = 0.0f;
-  lhs[0u][1u] = rhs;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Matrix_Assign_Scalar_DynamicIndices) {
-    auto* col = IndexAccessor("lhs", "col");
-    auto* el = IndexAccessor(col, "row");
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.mat4x2<f32>())),
-             Decl(Var("rhs", ty.f32())),
-             Decl(Var("col", ty.u32())),
-             Decl(Var("row", ty.u32())),
-             Assign(el, "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void set_matrix_scalar(inout float4x2 mat, int col, int row, float val) {
-  switch (col) {
-    case 0:
-      mat[0] = (row.xx == int2(0, 1)) ? val.xx : mat[0];
-      break;
-    case 1:
-      mat[1] = (row.xx == int2(0, 1)) ? val.xx : mat[1];
-      break;
-    case 2:
-      mat[2] = (row.xx == int2(0, 1)) ? val.xx : mat[2];
-      break;
-    case 3:
-      mat[3] = (row.xx == int2(0, 1)) ? val.xx : mat[3];
-      break;
-  }
-}
-
-void fn() {
-  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
-  float rhs = 0.0f;
-  uint col = 0u;
-  uint row = 0u;
-  set_matrix_scalar(lhs, col, row, rhs);
-}
-)");
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Assignment to composites of f16
-// See crbug.com/tint/2146
-////////////////////////////////////////////////////////////////////////////////
-TEST_F(HlslASTPrinterTest_Assign, Emit_Vector_f16_Assign) {
-    Enable(wgsl::Extension::kF16);
-
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.vec3<f16>())),
-             Decl(Var("rhs", ty.f16())),
-             Decl(Let("index", ty.u32(), Expr(0_u))),
-             Assign(IndexAccessor("lhs", "index"), "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(gen.Result(),
-              R"(void set_vector_element(inout vector<float16_t, 3> vec, int idx, float16_t val) {
-  vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;
-}
-
-void fn() {
-  vector<float16_t, 3> lhs = vector<float16_t, 3>(float16_t(0.0h), float16_t(0.0h), float16_t(0.0h));
-  float16_t rhs = float16_t(0.0h);
-  uint index = 0u;
-  set_vector_element(lhs, index, rhs);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Matrix_f16_Assign_Vector) {
-    Enable(wgsl::Extension::kF16);
-
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.mat4x2<f16>())),
-             Decl(Var("rhs", ty.vec2<f16>())),
-             Decl(Let("index", ty.u32(), Expr(0_u))),
-             Assign(IndexAccessor("lhs", "index"), "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(
-        gen.Result(),
-        R"(void set_matrix_column(inout matrix<float16_t, 4, 2> mat, int col, vector<float16_t, 2> val) {
-  switch (col) {
-    case 0: mat[0] = val; break;
-    case 1: mat[1] = val; break;
-    case 2: mat[2] = val; break;
-    case 3: mat[3] = val; break;
-  }
-}
-
-void fn() {
-  matrix<float16_t, 4, 2> lhs = matrix<float16_t, 4, 2>(float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h));
-  vector<float16_t, 2> rhs = vector<float16_t, 2>(float16_t(0.0h), float16_t(0.0h));
-  uint index = 0u;
-  set_matrix_column(lhs, index, rhs);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Assign, Emit_Matrix_f16_Assign_Scalar) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* col = IndexAccessor("lhs", "col");
-    auto* el = IndexAccessor(col, "row");
-    Func("fn", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("lhs", ty.mat4x2<f16>())),
-             Decl(Var("rhs", ty.f16())),
-             Decl(Let("col", ty.u32(), Expr(0_u))),
-             Decl(Let("row", ty.u32(), Expr(1_u))),
-             Assign(el, "rhs"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate());
-    EXPECT_EQ(
-        gen.Result(),
-        R"(void set_matrix_scalar(inout matrix<float16_t, 4, 2> mat, int col, int row, float16_t val) {
-  switch (col) {
-    case 0:
-      mat[0] = (row.xx == int2(0, 1)) ? val.xx : mat[0];
-      break;
-    case 1:
-      mat[1] = (row.xx == int2(0, 1)) ? val.xx : mat[1];
-      break;
-    case 2:
-      mat[2] = (row.xx == int2(0, 1)) ? val.xx : mat[2];
-      break;
-    case 3:
-      mat[3] = (row.xx == int2(0, 1)) ? val.xx : mat[3];
-      break;
-  }
-}
-
-void fn() {
-  matrix<float16_t, 4, 2> lhs = matrix<float16_t, 4, 2>(float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h), float16_t(0.0h));
-  float16_t rhs = float16_t(0.0h);
-  uint col = 0u;
-  uint row = 1u;
-  set_matrix_scalar(lhs, col, row, rhs);
-}
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
deleted file mode 100644
index 453b544..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.cc
+++ /dev/null
@@ -1,5058 +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/hlsl/writer/ast_printer/ast_printer.h"
-
-#include <cmath>
-#include <functional>
-#include <iomanip>
-#include <utility>
-#include <vector>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/core/constant/splat.h"
-#include "src/tint/lang/core/constant/value.h"
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/array.h"
-#include "src/tint/lang/core/type/atomic.h"
-#include "src/tint/lang/core/type/depth_multisampled_texture.h"
-#include "src/tint/lang/core/type/multisampled_texture.h"
-#include "src/tint/lang/core/type/sampled_texture.h"
-#include "src/tint/lang/core/type/storage_texture.h"
-#include "src/tint/lang/core/type/texture_dimension.h"
-#include "src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.h"
-#include "src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.h"
-#include "src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.h"
-#include "src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.h"
-#include "src/tint/lang/hlsl/writer/ast_raise/pixel_local.h"
-#include "src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h"
-#include "src/tint/lang/hlsl/writer/common/option_helpers.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/internal_attribute.h"
-#include "src/tint/lang/wgsl/ast/interpolate_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h"
-#include "src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h"
-#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
-#include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
-#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
-#include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
-#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
-#include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
-#include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h"
-#include "src/tint/lang/wgsl/ast/transform/fold_constants.h"
-#include "src/tint/lang/wgsl/ast/transform/manager.h"
-#include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h"
-#include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h"
-#include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h"
-#include "src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h"
-#include "src/tint/lang/wgsl/ast/transform/remove_phonies.h"
-#include "src/tint/lang/wgsl/ast/transform/robustness.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
-#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
-#include "src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
-#include "src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.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/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/switch_statement.h"
-#include "src/tint/lang/wgsl/sem/value_constructor.h"
-#include "src/tint/lang/wgsl/sem/value_conversion.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/containers/transform.h"
-#include "src/tint/utils/ice/ice.h"
-#include "src/tint/utils/macros/compiler.h"
-#include "src/tint/utils/macros/defer.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-#include "src/tint/utils/rtti/switch.h"
-#include "src/tint/utils/strconv/float_to_string.h"
-#include "src/tint/utils/text/string.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-const char kTempNamePrefix[] = "tint_tmp";
-
-static constexpr std::array<char, 4> kSwizzle = {'x', 'y', 'z', 'w'};
-
-const char* ImageFormatToRWtextureType(core::TexelFormat image_format) {
-    switch (image_format) {
-        case core::TexelFormat::kR8Unorm:
-        case core::TexelFormat::kBgra8Unorm:
-        case core::TexelFormat::kRgba8Unorm:
-        case core::TexelFormat::kRgba8Snorm:
-        case core::TexelFormat::kRgba16Float:
-        case core::TexelFormat::kR32Float:
-        case core::TexelFormat::kRg32Float:
-        case core::TexelFormat::kRgba32Float:
-            return "float4";
-        case core::TexelFormat::kRgba8Uint:
-        case core::TexelFormat::kRgba16Uint:
-        case core::TexelFormat::kR32Uint:
-        case core::TexelFormat::kRg32Uint:
-        case core::TexelFormat::kRgba32Uint:
-            return "uint4";
-        case core::TexelFormat::kRgba8Sint:
-        case core::TexelFormat::kRgba16Sint:
-        case core::TexelFormat::kR32Sint:
-        case core::TexelFormat::kRg32Sint:
-        case core::TexelFormat::kRgba32Sint:
-            return "int4";
-        default:
-            return nullptr;
-    }
-}
-
-void PrintF32(StringStream& out, float value) {
-    if (std::isinf(value)) {
-        out << "0.0f " << (value >= 0 ? "/* inf */" : "/* -inf */");
-    } else if (std::isnan(value)) {
-        out << "0.0f /* nan */";
-    } else {
-        out << tint::strconv::FloatToString(value) << "f";
-    }
-}
-
-void PrintF16(StringStream& out, float value) {
-    if (std::isinf(value)) {
-        out << "0.0h " << (value >= 0 ? "/* inf */" : "/* -inf */");
-    } else if (std::isnan(value)) {
-        out << "0.0h /* nan */";
-    } else {
-        out << tint::strconv::FloatToString(value) << "h";
-    }
-}
-
-// Helper for writing " : register(RX, spaceY)", where R is the register, X is
-// the binding point binding value, and Y is the binding point group value.
-struct RegisterAndSpace {
-    RegisterAndSpace(char r, BindingPoint bp) : reg(r), binding_point(bp) {}
-
-    const char reg;
-    BindingPoint const binding_point;
-};
-
-StringStream& operator<<(StringStream& s, const RegisterAndSpace& rs) {
-    s << " : register(" << rs.reg << rs.binding_point.binding;
-    // Omit the space if it's 0, as it's the default.
-    // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better compatibility.
-    if (rs.binding_point.group == 0) {
-        s << ")";
-    } else {
-        s << ", space" << rs.binding_point.group << ")";
-    }
-    return s;
-}
-
-struct VectorConstructorInfo {
-    const sem::Call* call = nullptr;
-    const sem::ValueConstructor* ctor = nullptr;
-    explicit operator bool() const { return call != nullptr; }
-};
-VectorConstructorInfo AsVectorConstructor(const sem::ValueExpression* expr) {
-    if (auto* call = expr->As<sem::Call>()) {
-        if (auto* ctor = call->Target()->As<sem::ValueConstructor>()) {
-            if (ctor->ReturnType()->Is<core::type::Vector>()) {
-                return {call, ctor};
-            }
-        }
-    }
-    return {};
-}
-
-const sem::ValueExpression* Zero(ProgramBuilder& b,
-                                 const core::type::Type* ty,
-                                 const sem::Statement* stmt) {
-    const ast::Expression* expr = nullptr;
-    if (ty->Is<core::type::I32>()) {
-        expr = b.Expr(0_i);
-    } else if (ty->Is<core::type::U32>()) {
-        expr = b.Expr(0_u);
-    } else if (ty->Is<core::type::F32>()) {
-        expr = b.Expr(0_f);
-    } else if (ty->Is<core::type::Bool>()) {
-        expr = b.Expr(false);
-    } else {
-        TINT_UNREACHABLE() << "unsupported vector element type: " << ty->TypeInfo().name;
-    }
-    auto* sem = b.create<sem::ValueExpression>(expr, ty, core::EvaluationStage::kRuntime, stmt,
-                                               /* constant_value */ nullptr,
-                                               /* has_side_effects */ false);
-    b.Sem().Add(expr, sem);
-    return sem;
-}
-
-const sem::Call* AppendVector(ProgramBuilder* b,
-                              const ast::Expression* vector_ast,
-                              const ast::Expression* scalar_ast) {
-    uint32_t packed_size;
-    const core::type::Type* packed_el_sem_ty;
-    auto* vector_sem = b->Sem().GetVal(vector_ast);
-    auto* scalar_sem = b->Sem().GetVal(scalar_ast);
-    auto* vector_ty = vector_sem->Type()->UnwrapRef();
-    if (auto* vec = vector_ty->As<core::type::Vector>()) {
-        packed_size = vec->Width() + 1;
-        packed_el_sem_ty = vec->Type();
-    } else {
-        packed_size = 2;
-        packed_el_sem_ty = vector_ty;
-    }
-
-    auto packed_el_ast_ty = Switch(
-        packed_el_sem_ty,  //
-        [&](const core::type::I32*) { return b->ty.i32(); },
-        [&](const core::type::U32*) { return b->ty.u32(); },
-        [&](const core::type::F32*) { return b->ty.f32(); },
-        [&](const core::type::Bool*) { return b->ty.bool_(); },  //
-        TINT_ICE_ON_NO_MATCH);
-
-    auto* statement = vector_sem->Stmt();
-
-    auto packed_ast_ty = b->ty.vec(packed_el_ast_ty, packed_size);
-    auto* packed_sem_ty = b->create<core::type::Vector>(packed_el_sem_ty, packed_size);
-
-    // If the coordinates are already passed in a vector constructor, with only
-    // scalar components supplied, extract the elements into the new vector
-    // instead of nesting a vector-in-vector.
-    // If the coordinates are a zero-constructor of the vector, then expand that
-    // to scalar zeros.
-    // The other cases for a nested vector constructor are when it is used
-    // to convert a vector of a different type, e.g. vec2<i32>(vec2<u32>()).
-    // In that case, preserve the original argument, or you'll get a type error.
-
-    Vector<const sem::ValueExpression*, 4> packed;
-    if (auto vc = AsVectorConstructor(vector_sem)) {
-        const auto num_supplied = vc.call->Arguments().Length();
-        if (num_supplied == 0) {
-            // Zero-value vector constructor. Populate with zeros
-            for (uint32_t i = 0; i < packed_size - 1; i++) {
-                auto* zero = Zero(*b, packed_el_sem_ty, statement);
-                packed.Push(zero);
-            }
-        } else if (num_supplied + 1 == packed_size) {
-            // All vector components were supplied as scalars.  Pass them through.
-            packed = vc.call->Arguments();
-        }
-    }
-    if (packed.IsEmpty()) {
-        // The special cases didn't occur. Use the vector argument as-is.
-        packed.Push(vector_sem);
-    }
-
-    if (packed_el_sem_ty != scalar_sem->Type()->UnwrapRef()) {
-        // Cast scalar to the vector element type
-        auto* scalar_cast_ast = b->Call(packed_el_ast_ty, scalar_ast);
-        auto* param = b->create<sem::Parameter>(nullptr, 0u, scalar_sem->Type()->UnwrapRef());
-        auto* scalar_cast_target = b->create<sem::ValueConversion>(packed_el_sem_ty, param,
-                                                                   core::EvaluationStage::kRuntime);
-        auto* scalar_cast_sem = b->create<sem::Call>(
-            scalar_cast_ast, scalar_cast_target, core::EvaluationStage::kRuntime,
-            Vector<const sem::ValueExpression*, 1>{scalar_sem}, statement,
-            /* constant_value */ nullptr, /* has_side_effects */ false);
-        b->Sem().Add(scalar_cast_ast, scalar_cast_sem);
-        packed.Push(scalar_cast_sem);
-    } else {
-        packed.Push(scalar_sem);
-    }
-
-    auto* ctor_ast =
-        b->Call(packed_ast_ty, tint::Transform(packed, [&](const sem::ValueExpression* expr) {
-                    return expr->Declaration();
-                }));
-    auto* ctor_target = b->create<sem::ValueConstructor>(
-        packed_sem_ty,
-        tint::Transform(packed,
-                        [&](const tint::sem::ValueExpression* arg, size_t i) {
-                            return b->create<sem::Parameter>(nullptr, static_cast<uint32_t>(i),
-                                                             arg->Type()->UnwrapRef());
-                        }),
-        core::EvaluationStage::kRuntime);
-    auto* ctor_sem = b->create<sem::Call>(ctor_ast, ctor_target, core::EvaluationStage::kRuntime,
-                                          std::move(packed), statement,
-                                          /* constant_value */ nullptr,
-                                          /* has_side_effects */ false);
-    b->Sem().Add(ctor_ast, ctor_sem);
-    return ctor_sem;
-}
-
-bool CheckSupportedExtensions(std::string_view writer_name,
-                              const ast::Module& module,
-                              diag::List& diags,
-                              VectorRef<wgsl::Extension> supported) {
-    Hashset<wgsl::Extension, 32> set;
-    for (auto ext : supported) {
-        set.Add(ext);
-    }
-
-    for (auto* enable : module.Enables()) {
-        for (auto* ext : enable->extensions) {
-            if (!set.Contains(ext->name)) {
-                diags.AddError(ext->source) << writer_name << " backend does not support extension "
-                                            << style::Code(ext->name);
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-}  // namespace
-
-SanitizedResult::SanitizedResult() = default;
-SanitizedResult::~SanitizedResult() = default;
-SanitizedResult::SanitizedResult(SanitizedResult&&) = default;
-
-SanitizedResult Sanitize(const Program& in, const Options& options) {
-    ast::transform::Manager manager;
-    ast::transform::DataMap data;
-
-    manager.Add<ast::transform::FoldConstants>();
-
-    manager.Add<ast::transform::DisableUniformityAnalysis>();
-
-    // ExpandCompoundAssignment must come before BuiltinPolyfill
-    manager.Add<ast::transform::ExpandCompoundAssignment>();
-
-    manager.Add<ast::transform::Unshadow>();  // Must come before DirectVariableAccess
-
-    // LocalizeStructArrayAssignment must come after:
-    // * SimplifyPointers, because it assumes assignment to arrays in structs are
-    // done directly, not indirectly.
-    // TODO(crbug.com/tint/1340): See if we can get rid of the duplicate
-    // SimplifyPointers transform. Can't do it right now because
-    // LocalizeStructArrayAssignment introduces pointers.
-    manager.Add<ast::transform::SimplifyPointers>();
-    manager.Add<LocalizeStructArrayAssignment>();
-
-    manager.Add<ast::transform::PromoteSideEffectsToDecl>();
-
-    if (!options.disable_robustness) {
-        // Robustness must come after PromoteSideEffectsToDecl
-        // Robustness must come before BuiltinPolyfill and CanonicalizeEntryPointIO
-        manager.Add<ast::transform::Robustness>();
-
-        ast::transform::Robustness::Config config = {};
-        config.bindings_ignored = std::unordered_set<BindingPoint>(
-            options.bindings.ignored_by_robustness_transform.cbegin(),
-            options.bindings.ignored_by_robustness_transform.cend());
-
-        // Direct3D guarantees to return zero for any resource that is accessed out of bounds, and
-        // according to the description of the assembly store_uav_typed, out of bounds addressing
-        // means nothing gets written to memory.
-        config.texture_action = ast::transform::Robustness::Action::kIgnore;
-
-        data.Add<ast::transform::Robustness::Config>(config);
-    }
-
-    tint::transform::multiplanar::BindingsMap multiplanar_map{};
-    RemapperData remapper_data{};
-    ArrayLengthFromUniformOptions array_length_from_uniform_options{};
-    PopulateBindingRelatedOptions(options, remapper_data, multiplanar_map,
-                                  array_length_from_uniform_options);
-
-    manager.Add<ast::transform::BindingRemapper>();
-    // D3D11 and 12 registers like `t3` and `c3` have the same bindingOffset number in
-    // the remapping but should not be considered a collision because they have
-    // different types.
-    data.Add<ast::transform::BindingRemapper::Remappings>(
-        remapper_data, options.bindings.access_controls, /* allow_collisions */ true);
-
-    // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
-    // MultiplanarExternalTexture must come after BindingRemapper
-    data.Add<ast::transform::MultiplanarExternalTexture::NewBindingPoints>(multiplanar_map,
-                                                                           /* may_collide */ true);
-    manager.Add<ast::transform::MultiplanarExternalTexture>();
-
-    {  // Builtin polyfills
-        ast::transform::BuiltinPolyfill::Builtins polyfills;
-        polyfills.acosh = ast::transform::BuiltinPolyfill::Level::kFull;
-        polyfills.asinh = true;
-        polyfills.atanh = ast::transform::BuiltinPolyfill::Level::kFull;
-        polyfills.bitshift_modulo = true;
-        polyfills.clamp_int = true;
-        // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
-        // and `firstbithigh`.
-        polyfills.conv_f32_to_iu32 = true;
-        polyfills.count_leading_zeros = true;
-        polyfills.count_trailing_zeros = true;
-        polyfills.extract_bits = ast::transform::BuiltinPolyfill::Level::kFull;
-        polyfills.first_leading_bit = true;
-        polyfills.first_trailing_bit = true;
-        polyfills.fwidth_fine = true;
-        polyfills.insert_bits = ast::transform::BuiltinPolyfill::Level::kFull;
-        polyfills.int_div_mod = !options.disable_polyfill_integer_div_mod;
-        polyfills.precise_float_mod = true;
-        polyfills.reflect_vec2_f32 = options.polyfill_reflect_vec2_f32;
-        polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
-        polyfills.workgroup_uniform_load = true;
-        polyfills.dot_4x8_packed = options.polyfill_dot_4x8_packed;
-        polyfills.pack_unpack_4x8 = options.polyfill_pack_unpack_4x8;
-        // Currently Pack4xU8Clamp() must be polyfilled because on latest DXC pack_clamp_u8()
-        // receives an int32_t4 as its input.
-        // See https://github.com/microsoft/DirectXShaderCompiler/issues/5091 for more details.
-        polyfills.pack_4xu8_clamp = true;
-        data.Add<ast::transform::BuiltinPolyfill::Config>(polyfills);
-        manager.Add<ast::transform::BuiltinPolyfill>();  // Must come before DirectVariableAccess
-    }
-
-    manager.Add<ast::transform::DirectVariableAccess>();
-
-    if (!options.disable_workgroup_init) {
-        // ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
-        // ZeroInitWorkgroupMemory may inject new builtin parameters.
-        manager.Add<ast::transform::ZeroInitWorkgroupMemory>();
-    }
-
-    {
-        PixelLocal::Config cfg;
-        for (auto it : options.pixel_local.attachments) {
-            uint32_t member_index = it.first;
-            auto& attachment = it.second;
-
-            cfg.pls_member_to_rov_reg.Add(member_index, attachment.index);
-
-            core::TexelFormat format = core::TexelFormat::kUndefined;
-            switch (attachment.format) {
-                case PixelLocalAttachment::TexelFormat::kR32Sint:
-                    format = core::TexelFormat::kR32Sint;
-                    break;
-                case PixelLocalAttachment::TexelFormat::kR32Uint:
-                    format = core::TexelFormat::kR32Uint;
-                    break;
-                case PixelLocalAttachment::TexelFormat::kR32Float:
-                    format = core::TexelFormat::kR32Float;
-                    break;
-                default:
-                    TINT_ICE() << "missing texel format for pixel local storage attachment";
-            }
-            cfg.pls_member_to_rov_format.Add(member_index, format);
-        }
-        cfg.rov_group_index = options.pixel_local.group_index;
-        data.Add<PixelLocal::Config>(cfg);
-        manager.Add<PixelLocal>();
-    }
-
-    // CanonicalizeEntryPointIO must come after Robustness
-    manager.Add<ast::transform::CanonicalizeEntryPointIO>();
-
-    if (options.truncate_interstage_variables) {
-        // When interstage_locations is empty, it means there's no user-defined interstage variables
-        // being used in the next stage. Still, HLSL compiler register mismatch could happen, if
-        // there's builtin inputs used in the next stage. So we still run
-        // TruncateInterstageVariables transform.
-
-        // TruncateInterstageVariables itself will skip when interstage_locations matches exactly
-        // with the current stage output.
-
-        // Build the config for internal TruncateInterstageVariables transform.
-        TruncateInterstageVariables::Config truncate_interstage_variables_cfg;
-        truncate_interstage_variables_cfg.interstage_locations =
-            std::move(options.interstage_locations);
-        manager.Add<TruncateInterstageVariables>();
-        data.Add<TruncateInterstageVariables::Config>(std::move(truncate_interstage_variables_cfg));
-    }
-
-    // NumWorkgroupsFromUniform must come after CanonicalizeEntryPointIO, as it
-    // assumes that num_workgroups builtins only appear as struct members and are
-    // only accessed directly via member accessors.
-    manager.Add<NumWorkgroupsFromUniform>();
-    manager.Add<ast::transform::VectorizeScalarMatrixInitializers>();
-    manager.Add<ast::transform::SimplifyPointers>();
-    manager.Add<ast::transform::RemovePhonies>();
-
-    // DemoteToHelper must come after CanonicalizeEntryPointIO, PromoteSideEffectsToDecl, and
-    // ExpandCompoundAssignment.
-    // TODO(crbug.com/42250787): This is only necessary when FXC is being used.
-    if (options.compiler == tint::hlsl::writer::Options::Compiler::kFXC) {
-        manager.Add<ast::transform::DemoteToHelper>();
-    }
-
-    // ArrayLengthFromUniform must come after SimplifyPointers as it assumes that the form of the
-    // array length argument is &var.array.
-    manager.Add<ast::transform::ArrayLengthFromUniform>();
-    // Build the config for the internal ArrayLengthFromUniform transform.
-    ast::transform::ArrayLengthFromUniform::Config array_length_from_uniform_cfg(
-        BindingPoint{array_length_from_uniform_options.ubo_binding.group,
-                     array_length_from_uniform_options.ubo_binding.binding});
-    array_length_from_uniform_cfg.bindpoint_to_size_index =
-        std::move(array_length_from_uniform_options.bindpoint_to_size_index);
-    data.Add<ast::transform::ArrayLengthFromUniform::Config>(
-        std::move(array_length_from_uniform_cfg));
-    // DecomposeMemoryAccess must come after:
-    // * SimplifyPointers, as we cannot take the address of calls to
-    //   DecomposeMemoryAccess::Intrinsic and we need to fold away the address-of and dereferences
-    //   of `*(&(intrinsic_load()))` expressions.
-    // * RemovePhonies, as phonies can be assigned a pointer to a
-    //   non-constructible buffer, or dynamic array, which DMA cannot cope with.
-    manager.Add<DecomposeMemoryAccess>();
-    // CalculateArrayLength must come after DecomposeMemoryAccess, as
-    // DecomposeMemoryAccess special-cases the arrayLength() intrinsic, which
-    // will be transformed by CalculateArrayLength
-    manager.Add<CalculateArrayLength>();
-    manager.Add<ast::transform::PromoteInitializersToLet>();
-
-    manager.Add<ast::transform::RemoveContinueInSwitch>();
-
-    manager.Add<ast::transform::AddEmptyEntryPoint>();
-
-    data.Add<NumWorkgroupsFromUniform::Config>(options.root_constant_binding_point);
-
-    SanitizedResult result;
-    ast::transform::DataMap outputs;
-    result.program = manager.Run(in, data, outputs);
-    if (auto* res = outputs.Get<ast::transform::ArrayLengthFromUniform::Result>()) {
-        result.used_array_length_from_uniform_indices = std::move(res->used_size_indices);
-    }
-    return result;
-}
-
-ASTPrinter::ASTPrinter(const Program& program) : builder_(ProgramBuilder::Wrap(program)) {}
-
-ASTPrinter::~ASTPrinter() = default;
-
-bool ASTPrinter::Generate() {
-    if (!CheckSupportedExtensions("HLSL", builder_.AST(), diagnostics_,
-                                  Vector{
-                                      wgsl::Extension::kChromiumDisableUniformityAnalysis,
-                                      wgsl::Extension::kChromiumExperimentalPixelLocal,
-                                      wgsl::Extension::kChromiumExperimentalImmediate,
-                                      wgsl::Extension::kChromiumInternalGraphite,
-                                      wgsl::Extension::kClipDistances,
-                                      wgsl::Extension::kF16,
-                                      wgsl::Extension::kDualSourceBlending,
-                                      wgsl::Extension::kSubgroups,
-                                  })) {
-        return false;
-    }
-
-    // Goldilocks logic to add lines between top level declarations
-    const tint::TypeInfo* last_kind = nullptr;
-    size_t last_padding_line = 0;
-    auto EmitPaddingLine = [&](const tint::TypeInfo* kind, bool is_function) {
-        // Don't emit double line breaks
-        if (current_buffer_->lines.size() == last_padding_line) {
-            last_kind = kind;
-            return;
-        }
-
-        if (is_function || (last_kind && last_kind != kind)) {
-            Line();
-            last_padding_line = current_buffer_->lines.size();
-            last_kind = kind;
-            global_insertion_point_ = current_buffer_->lines.size();
-        }
-    };
-
-    auto* mod = builder_.Sem().Module();
-    for (auto* decl : mod->DependencyOrderedDeclarations()) {
-        if (decl->IsAnyOf<ast::Alias, ast::DiagnosticDirective, ast::Enable, ast::Requires,
-                          ast::ConstAssert>()) {
-            continue;  // These are not emitted.
-        }
-
-        global_insertion_point_ = current_buffer_->lines.size();
-
-        bool ok = Switch(
-            decl,
-            [&](const ast::Variable* global) {
-                EmitPaddingLine(&decl->TypeInfo(), false);
-                return EmitGlobalVariable(global);
-            },
-            [&](const ast::Struct* str) {
-                auto* ty = builder_.Sem().Get(str);
-                auto address_space_uses = ty->AddressSpaceUsage();
-                if (address_space_uses.Count() !=
-                    ((address_space_uses.Contains(core::AddressSpace::kStorage) ? 1u : 0u) +
-                     (address_space_uses.Contains(core::AddressSpace::kUniform) ? 1u : 0u))) {
-                    // The structure is used as something other than a storage buffer or
-                    // uniform buffer, so it needs to be emitted.
-                    // Storage buffer are read and written to via a ByteAddressBuffer
-                    // instead of true structure.
-                    // Structures used as uniform buffer are read from an array of
-                    // vectors instead of true structure.
-                    EmitPaddingLine(&decl->TypeInfo(), false);
-                    return EmitStructType(current_buffer_, ty, str->members);
-                }
-                return true;
-            },
-            [&](const ast::Function* func) {
-                EmitPaddingLine(&decl->TypeInfo(), true);
-                if (func->IsEntryPoint()) {
-                    return EmitEntryPointFunction(func);
-                }
-                return EmitFunction(func);
-            },  //
-            TINT_ICE_ON_NO_MATCH);
-
-        if (!ok) {
-            return false;
-        }
-    }
-
-    if (!helpers_.lines.empty()) {
-        current_buffer_->Insert(helpers_, 0, 0);
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitDynamicVectorAssignment(const ast::AssignmentStatement* stmt,
-                                             const core::type::Vector* vec) {
-    auto name = tint::GetOrAdd(dynamic_vector_write_, vec, [&]() -> std::string {
-        std::string fn = UniqueIdentifier("set_vector_element");
-        {
-            auto out = Line(&helpers_);
-            out << "void " << fn << "(inout ";
-            if (!EmitTypeAndName(out, vec, core::AddressSpace::kUndefined, core::Access::kUndefined,
-                                 "vec")) {
-                return "";
-            }
-            out << ", int idx, ";
-            if (!EmitTypeAndName(out, vec->Type(), core::AddressSpace::kUndefined,
-                                 core::Access::kUndefined, "val")) {
-                return "";
-            }
-            out << ") {";
-        }
-        {
-            ScopedIndent si(&helpers_);
-            auto out = Line(&helpers_);
-            switch (vec->Width()) {
-                case 2:
-                    out << "vec = (idx.xx == int2(0, 1)) ? val.xx : vec;";
-                    break;
-                case 3:
-                    out << "vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;";
-                    break;
-                case 4:
-                    out << "vec = (idx.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : vec;";
-                    break;
-                default:
-                    TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
-            }
-        }
-        Line(&helpers_) << "}";
-        Line(&helpers_);
-        return fn;
-    });
-
-    if (name.empty()) {
-        return false;
-    }
-
-    auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
-
-    auto out = Line();
-    out << name << "(";
-    if (!EmitExpression(out, ast_access_expr->object)) {
-        return false;
-    }
-    out << ", ";
-    if (!EmitExpression(out, ast_access_expr->index)) {
-        return false;
-    }
-    out << ", ";
-    if (!EmitExpression(out, stmt->rhs)) {
-        return false;
-    }
-    out << ");";
-
-    return true;
-}
-
-bool ASTPrinter::EmitDynamicMatrixVectorAssignment(const ast::AssignmentStatement* stmt,
-                                                   const core::type::Matrix* mat) {
-    auto name = tint::GetOrAdd(dynamic_matrix_vector_write_, mat, [&]() -> std::string {
-        std::string fn = UniqueIdentifier("set_matrix_column");
-        {
-            auto out = Line(&helpers_);
-            out << "void " << fn << "(inout ";
-            if (!EmitTypeAndName(out, mat, core::AddressSpace::kUndefined, core::Access::kUndefined,
-                                 "mat")) {
-                return "";
-            }
-            out << ", int col, ";
-            if (!EmitTypeAndName(out, mat->ColumnType(), core::AddressSpace::kUndefined,
-                                 core::Access::kUndefined, "val")) {
-                return "";
-            }
-            out << ") {";
-        }
-        {
-            ScopedIndent si(&helpers_);
-            Line(&helpers_) << "switch (col) {";
-            {
-                ScopedIndent si2(&helpers_);
-                for (uint32_t i = 0; i < mat->Columns(); ++i) {
-                    Line(&helpers_) << "case " << i << ": mat[" << i << "] = val; break;";
-                }
-            }
-            Line(&helpers_) << "}";
-        }
-        Line(&helpers_) << "}";
-        Line(&helpers_);
-        return fn;
-    });
-
-    if (name.empty()) {
-        return false;
-    }
-
-    auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
-
-    auto out = Line();
-    out << name << "(";
-    if (!EmitExpression(out, ast_access_expr->object)) {
-        return false;
-    }
-    out << ", ";
-    if (!EmitExpression(out, ast_access_expr->index)) {
-        return false;
-    }
-    out << ", ";
-    if (!EmitExpression(out, stmt->rhs)) {
-        return false;
-    }
-    out << ");";
-
-    return true;
-}
-
-bool ASTPrinter::EmitDynamicMatrixScalarAssignment(const ast::AssignmentStatement* stmt,
-                                                   const core::type::Matrix* mat) {
-    auto* lhs_row_access = stmt->lhs->As<ast::IndexAccessorExpression>();
-    auto* lhs_col_access = lhs_row_access->object->As<ast::IndexAccessorExpression>();
-
-    auto name = tint::GetOrAdd(dynamic_matrix_scalar_write_, mat, [&]() -> std::string {
-        std::string fn = UniqueIdentifier("set_matrix_scalar");
-        {
-            auto out = Line(&helpers_);
-            out << "void " << fn << "(inout ";
-            if (!EmitTypeAndName(out, mat, core::AddressSpace::kUndefined, core::Access::kUndefined,
-                                 "mat")) {
-                return "";
-            }
-            out << ", int col, int row, ";
-            if (!EmitTypeAndName(out, mat->Type(), core::AddressSpace::kUndefined,
-                                 core::Access::kUndefined, "val")) {
-                return "";
-            }
-            out << ") {";
-        }
-        {
-            ScopedIndent si(&helpers_);
-            Line(&helpers_) << "switch (col) {";
-            {
-                ScopedIndent si2(&helpers_);
-                for (uint32_t i = 0; i < mat->Columns(); ++i) {
-                    Line(&helpers_) << "case " << i << ":";
-                    {
-                        auto vec_name = "mat[" + std::to_string(i) + "]";
-                        ScopedIndent si3(&helpers_);
-                        {
-                            auto out = Line(&helpers_);
-                            switch (mat->Rows()) {
-                                case 2:
-                                    out << vec_name
-                                        << " = (row.xx == int2(0, 1)) ? val.xx : " << vec_name
-                                        << ";";
-                                    break;
-                                case 3:
-                                    out << vec_name
-                                        << " = (row.xxx == int3(0, 1, 2)) ? val.xxx : " << vec_name
-                                        << ";";
-                                    break;
-                                case 4:
-                                    out << vec_name
-                                        << " = (row.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : "
-                                        << vec_name << ";";
-                                    break;
-                                default: {
-                                    auto* vec = TypeOf(lhs_row_access->object)
-                                                    ->UnwrapPtrOrRef()
-                                                    ->As<core::type::Vector>();
-                                    TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
-                                }
-                            }
-                        }
-                        Line(&helpers_) << "break;";
-                    }
-                }
-            }
-            Line(&helpers_) << "}";
-        }
-        Line(&helpers_) << "}";
-        Line(&helpers_);
-        return fn;
-    });
-
-    if (name.empty()) {
-        return false;
-    }
-
-    auto out = Line();
-    out << name << "(";
-    if (!EmitExpression(out, lhs_col_access->object)) {
-        return false;
-    }
-    out << ", ";
-    if (!EmitExpression(out, lhs_col_access->index)) {
-        return false;
-    }
-    out << ", ";
-    if (!EmitExpression(out, lhs_row_access->index)) {
-        return false;
-    }
-    out << ", ";
-    if (!EmitExpression(out, stmt->rhs)) {
-        return false;
-    }
-    out << ");";
-
-    return true;
-}
-
-bool ASTPrinter::EmitIndexAccessor(StringStream& out, const ast::IndexAccessorExpression* expr) {
-    if (!EmitExpression(out, expr->object)) {
-        return false;
-    }
-    out << "[";
-
-    if (!EmitExpression(out, expr->index)) {
-        return false;
-    }
-    out << "]";
-
-    return true;
-}
-
-bool ASTPrinter::EmitBitcastCall(StringStream& out, const ast::CallExpression* call) {
-    auto* arg = call->args[0];
-    auto* src_type = TypeOf(arg)->UnwrapRef();
-    auto* dst_type = TypeOf(call);
-
-    auto* src_el_type = src_type->DeepestElement();
-    auto* dst_el_type = dst_type->DeepestElement();
-
-    if (!dst_el_type->IsIntegerScalar() && !dst_el_type->IsFloatScalar()) {
-        diagnostics_.AddError(Source{})
-            << "Unable to do bitcast to type " << dst_el_type->FriendlyName();
-        return false;
-    }
-
-    // Handle identity bitcast.
-    if (src_type == dst_type) {
-        return EmitExpression(out, arg);
-    }
-
-    // Handle the f16 types using polyfill functions
-    if (src_el_type->Is<core::type::F16>() || dst_el_type->Is<core::type::F16>()) {
-        auto f16_bitcast_polyfill = [&]() {
-            if (src_el_type->Is<core::type::F16>()) {
-                // Source type must be vec2<f16> or vec4<f16>, since type f16 and vec3<f16> can only
-                // have identity bitcast.
-                auto* src_vec = src_type->As<core::type::Vector>();
-                TINT_ASSERT(src_vec);
-                TINT_ASSERT(((src_vec->Width() == 2u) || (src_vec->Width() == 4u)));
-
-                // Bitcast f16 types to others by converting the given f16 value to f32 and call
-                // f32tof16 to get the bits. This should be safe, because the convertion is precise
-                // for finite and infinite f16 value as they are exactly representable by f32, and
-                // WGSL spec allow any result if f16 value is NaN.
-                return tint::GetOrAdd(
-                    bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
-                        TextBuffer b;
-                        TINT_DEFER(helpers_.Append(b));
-
-                        auto fn_name = UniqueIdentifier(std::string("tint_bitcast_from_f16"));
-                        {
-                            auto decl = Line(&b);
-                            if (!EmitTypeAndName(decl, dst_type, core::AddressSpace::kUndefined,
-                                                 core::Access::kUndefined, fn_name)) {
-                                return "";
-                            }
-                            {
-                                ScopedParen sp(decl);
-                                if (!EmitTypeAndName(decl, src_type, core::AddressSpace::kUndefined,
-                                                     core::Access::kUndefined, "src")) {
-                                    return "";
-                                }
-                            }
-                            decl << " {";
-                        }
-                        {
-                            ScopedIndent si(&b);
-                            {
-                                Line(&b) << "uint" << src_vec->Width() << " r = f32tof16(float"
-                                         << src_vec->Width() << "(src));";
-
-                                {
-                                    auto s = Line(&b);
-                                    s << "return as";
-                                    if (!EmitType(s, dst_el_type, core::AddressSpace::kUndefined,
-                                                  core::Access::kReadWrite, "")) {
-                                        return "";
-                                    }
-                                    s << "(";
-                                    switch (src_vec->Width()) {
-                                        case 2: {
-                                            s << "uint((r.x & 0xffff) | ((r.y & 0xffff) << 16))";
-                                            break;
-                                        }
-                                        case 4: {
-                                            s << "uint2((r.x & 0xffff) | ((r.y & 0xffff) << 16), "
-                                                 "(r.z & 0xffff) | ((r.w & 0xffff) << 16))";
-                                            break;
-                                        }
-                                    }
-                                    s << ");";
-                                }
-                            }
-                        }
-                        Line(&b) << "}";
-                        Line(&b);
-                        return fn_name;
-                    });
-            } else {
-                // Destination type must be vec2<f16> or vec4<f16>.
-                auto* dst_vec = dst_type->As<core::type::Vector>();
-                TINT_ASSERT((dst_vec && ((dst_vec->Width() == 2u) || (dst_vec->Width() == 4u)) &&
-                             dst_el_type->Is<core::type::F16>()));
-                // Source type must be f32/i32/u32 or vec2<f32/i32/u32>.
-                auto* src_vec = src_type->As<core::type::Vector>();
-                TINT_ASSERT(
-                    (src_type->IsAnyOf<core::type::I32, core::type::U32, core::type::F32>() ||
-                     (src_vec && src_vec->Width() == 2u &&
-                      src_el_type->IsAnyOf<core::type::I32, core::type::U32, core::type::F32>())));
-                std::string src_type_suffix = (src_vec ? "2" : "");
-
-                // Bitcast other types to f16 types by reinterpreting their bits as f16 using
-                // f16tof32, and convert the result f32 to f16. This should be safe, because the
-                // convertion is precise for finite and infinite f16 result value as they are
-                // exactly representable by f32, and WGSL spec allow any result if f16 result value
-                // would be NaN.
-                return tint::GetOrAdd(
-                    bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
-                        TextBuffer b;
-                        TINT_DEFER(helpers_.Append(b));
-
-                        auto fn_name = UniqueIdentifier(std::string("tint_bitcast_to_f16"));
-                        {
-                            auto decl = Line(&b);
-                            if (!EmitTypeAndName(decl, dst_type, core::AddressSpace::kUndefined,
-                                                 core::Access::kUndefined, fn_name)) {
-                                return "";
-                            }
-                            {
-                                ScopedParen sp(decl);
-                                if (!EmitTypeAndName(decl, src_type, core::AddressSpace::kUndefined,
-                                                     core::Access::kUndefined, "src")) {
-                                    return "";
-                                }
-                            }
-                            decl << " {";
-                        }
-                        {
-                            ScopedIndent si(&b);
-                            {
-                                // Convert the source to uint for f16tof32.
-                                Line(&b) << "uint" << src_type_suffix << " v = asuint(src);";
-                                // Reinterprete the low 16 bits and high 16 bits
-                                Line(&b) << "float" << src_type_suffix
-                                         << " t_low = f16tof32(v & 0xffff);";
-                                Line(&b) << "float" << src_type_suffix
-                                         << " t_high = f16tof32((v >> 16) & 0xffff);";
-                                // Construct the result f16 vector
-                                {
-                                    auto s = Line(&b);
-                                    s << "return ";
-                                    if (!EmitType(s, dst_type, core::AddressSpace::kUndefined,
-                                                  core::Access::kReadWrite, "")) {
-                                        return "";
-                                    }
-                                    s << "(";
-                                    switch (dst_vec->Width()) {
-                                        case 2: {
-                                            s << "t_low.x, t_high.x";
-                                            break;
-                                        }
-                                        case 4: {
-                                            s << "t_low.x, t_high.x, t_low.y, t_high.y";
-                                            break;
-                                        }
-                                    }
-                                    s << ");";
-                                }
-                            }
-                        }
-                        Line(&b) << "}";
-                        Line(&b);
-                        return fn_name;
-                    });
-            }
-        };
-
-        // Get or create the polyfill
-        auto fn = f16_bitcast_polyfill();
-        if (fn.empty()) {
-            return false;
-        }
-        // Call the polyfill
-        out << fn;
-        {
-            ScopedParen sp(out);
-            if (!EmitExpression(out, arg)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    // Otherwise, bitcasting between non-f16 types.
-    TINT_ASSERT((!src_el_type->Is<core::type::F16>() && !dst_el_type->Is<core::type::F16>()));
-    out << "as";
-    if (!EmitType(out, dst_el_type, core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
-        return false;
-    }
-    out << "(";
-    if (!EmitExpression(out, arg)) {
-        return false;
-    }
-    out << ")";
-    return true;
-}
-
-bool ASTPrinter::EmitAssign(const ast::AssignmentStatement* stmt) {
-    if (auto* lhs_access = stmt->lhs->As<ast::IndexAccessorExpression>()) {
-        auto validate_obj_not_pointer = [&](const core::type::Type* object_ty) {
-            if (DAWN_UNLIKELY(object_ty->Is<core::type::Pointer>())) {
-                TINT_ICE() << "lhs of index accessor should not be a pointer. These should have "
-                              "been removed by transforms such as SimplifyPointers, "
-                              "DecomposeMemoryAccess, and DirectVariableAccess";
-            }
-            return true;
-        };
-
-        // BUG(crbug.com/tint/1333): work around assignment of scalar to matrices
-        // with at least one dynamic index
-        if (auto* lhs_sub_access = lhs_access->object->As<ast::IndexAccessorExpression>()) {
-            const auto* lhs_sub_access_type = TypeOf(lhs_sub_access->object);
-            if (!validate_obj_not_pointer(lhs_sub_access_type)) {
-                return false;
-            }
-            if (auto* mat = lhs_sub_access_type->UnwrapRef()->As<core::type::Matrix>()) {
-                auto* rhs_row_idx_sem = builder_.Sem().GetVal(lhs_access->index);
-                auto* rhs_col_idx_sem = builder_.Sem().GetVal(lhs_sub_access->index);
-                if (!rhs_row_idx_sem->ConstantValue() || !rhs_col_idx_sem->ConstantValue()) {
-                    return EmitDynamicMatrixScalarAssignment(stmt, mat);
-                }
-            }
-        }
-        // BUG(crbug.com/tint/1333): work around assignment of vector to matrices
-        // with dynamic indices
-        const auto* lhs_access_type = TypeOf(lhs_access->object);
-        if (!validate_obj_not_pointer(lhs_access_type)) {
-            return false;
-        }
-        if (auto* mat = lhs_access_type->UnwrapRef()->As<core::type::Matrix>()) {
-            auto* lhs_index_sem = builder_.Sem().GetVal(lhs_access->index);
-            if (!lhs_index_sem->ConstantValue()) {
-                return EmitDynamicMatrixVectorAssignment(stmt, mat);
-            }
-        }
-        // BUG(crbug.com/tint/534): work around assignment to vectors with dynamic
-        // indices
-        if (auto* vec = lhs_access_type->UnwrapRef()->As<core::type::Vector>()) {
-            auto* rhs_sem = builder_.Sem().GetVal(lhs_access->index);
-            if (!rhs_sem->ConstantValue()) {
-                return EmitDynamicVectorAssignment(stmt, vec);
-            }
-        }
-    }
-
-    auto out = Line();
-    if (!EmitExpression(out, stmt->lhs)) {
-        return false;
-    }
-    out << " = ";
-    if (!EmitExpression(out, stmt->rhs)) {
-        return false;
-    }
-    out << ";";
-    return true;
-}
-
-bool ASTPrinter::EmitBinary(StringStream& out, const ast::BinaryExpression* expr) {
-    if (expr->op == core::BinaryOp::kLogicalAnd || expr->op == core::BinaryOp::kLogicalOr) {
-        auto name = UniqueIdentifier(kTempNamePrefix);
-
-        {
-            auto pre = Line();
-            pre << "bool " << name << " = ";
-            if (!EmitExpression(pre, expr->lhs)) {
-                return false;
-            }
-            pre << ";";
-        }
-
-        if (expr->op == core::BinaryOp::kLogicalOr) {
-            Line() << "if (!" << name << ") {";
-        } else {
-            Line() << "if (" << name << ") {";
-        }
-
-        {
-            ScopedIndent si(this);
-            auto pre = Line();
-            pre << name << " = ";
-            if (!EmitExpression(pre, expr->rhs)) {
-                return false;
-            }
-            pre << ";";
-        }
-
-        Line() << "}";
-
-        out << "(" << name << ")";
-        return true;
-    }
-
-    auto* lhs_type = TypeOf(expr->lhs)->UnwrapRef();
-    auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
-    // Multiplying by a matrix requires the use of `mul` in order to get the
-    // type of multiply we desire.
-    if (expr->op == core::BinaryOp::kMultiply &&
-        ((lhs_type->Is<core::type::Vector>() && rhs_type->Is<core::type::Matrix>()) ||
-         (lhs_type->Is<core::type::Matrix>() && rhs_type->Is<core::type::Vector>()) ||
-         (lhs_type->Is<core::type::Matrix>() && rhs_type->Is<core::type::Matrix>()))) {
-        // Matrices are transposed, so swap LHS and RHS.
-        out << "mul(";
-        if (!EmitExpression(out, expr->rhs)) {
-            return false;
-        }
-        out << ", ";
-        if (!EmitExpression(out, expr->lhs)) {
-            return false;
-        }
-        out << ")";
-
-        return true;
-    }
-
-    ScopedParen sp(out);
-
-    if (!EmitExpression(out, expr->lhs)) {
-        return false;
-    }
-    out << " ";
-
-    switch (expr->op) {
-        case core::BinaryOp::kAnd:
-            out << "&";
-            break;
-        case core::BinaryOp::kOr:
-            out << "|";
-            break;
-        case core::BinaryOp::kXor:
-            out << "^";
-            break;
-        case core::BinaryOp::kLogicalAnd:
-        case core::BinaryOp::kLogicalOr: {
-            // These are both handled above.
-            TINT_UNREACHABLE();
-        }
-        case core::BinaryOp::kEqual:
-            out << "==";
-            break;
-        case core::BinaryOp::kNotEqual:
-            out << "!=";
-            break;
-        case core::BinaryOp::kLessThan:
-            out << "<";
-            break;
-        case core::BinaryOp::kGreaterThan:
-            out << ">";
-            break;
-        case core::BinaryOp::kLessThanEqual:
-            out << "<=";
-            break;
-        case core::BinaryOp::kGreaterThanEqual:
-            out << ">=";
-            break;
-        case core::BinaryOp::kShiftLeft:
-            out << "<<";
-            break;
-        case core::BinaryOp::kShiftRight:
-            // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
-            // implementation-defined behaviour for negative LHS.  We may have to
-            // generate extra code to implement WGSL-specified behaviour for negative
-            // LHS.
-            out << R"(>>)";
-            break;
-
-        case core::BinaryOp::kAdd:
-            out << "+";
-            break;
-        case core::BinaryOp::kSubtract:
-            out << "-";
-            break;
-        case core::BinaryOp::kMultiply:
-            out << "*";
-            break;
-        case core::BinaryOp::kDivide:
-            out << "/";
-            break;
-        case core::BinaryOp::kModulo:
-            out << "%";
-            break;
-    }
-    out << " ";
-
-    if (!EmitExpression(out, expr->rhs)) {
-        return false;
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitStatements(VectorRef<const ast::Statement*> stmts) {
-    for (auto* s : stmts) {
-        if (!EmitStatement(s)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool ASTPrinter::EmitStatementsWithIndent(VectorRef<const ast::Statement*> stmts) {
-    ScopedIndent si(this);
-    return EmitStatements(stmts);
-}
-
-bool ASTPrinter::EmitBlock(const ast::BlockStatement* stmt) {
-    Line() << "{";
-    if (!EmitStatementsWithIndent(stmt->statements)) {
-        return false;
-    }
-    Line() << "}";
-    return true;
-}
-
-bool ASTPrinter::EmitBreak(const ast::BreakStatement*) {
-    Line() << "break;";
-    return true;
-}
-
-bool ASTPrinter::EmitBreakIf(const ast::BreakIfStatement* b) {
-    auto out = Line();
-    out << "if (";
-    if (!EmitExpression(out, b->condition)) {
-        return false;
-    }
-    out << ") { break; }";
-    return true;
-}
-
-bool ASTPrinter::EmitCall(StringStream& out, const ast::CallExpression* expr) {
-    auto* call = builder_.Sem().Get<sem::Call>(expr);
-    auto* target = call->Target();
-    return Switch(
-        target,  //
-        [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
-        [&](const sem::BuiltinFn* builtin) { return EmitBuiltinCall(out, call, builtin); },
-        [&](const sem::ValueConversion* conv) { return EmitValueConversion(out, call, conv); },
-        [&](const sem::ValueConstructor* ctor) { return EmitValueConstructor(out, call, ctor); },
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitFunctionCall(StringStream& out,
-                                  const sem::Call* call,
-                                  const sem::Function* func) {
-    auto* expr = call->Declaration();
-
-    if (ast::HasAttribute<CalculateArrayLength::BufferSizeIntrinsic>(
-            func->Declaration()->attributes)) {
-        // Special function generated by the CalculateArrayLength transform for
-        // calling X.GetDimensions(Y)
-        if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
-            return false;
-        }
-        out << ".GetDimensions(";
-        if (!EmitExpression(out, call->Arguments()[1]->Declaration())) {
-            return false;
-        }
-        out << ")";
-        return true;
-    }
-
-    if (auto* intrinsic =
-            ast::GetAttribute<DecomposeMemoryAccess::Intrinsic>(func->Declaration()->attributes)) {
-        switch (intrinsic->address_space) {
-            case core::AddressSpace::kUniform:
-                return EmitUniformBufferAccess(out, expr, intrinsic);
-            case core::AddressSpace::kStorage:
-                if (!intrinsic->IsAtomic()) {
-                    return EmitStorageBufferAccess(out, expr, intrinsic);
-                }
-                break;
-            default:
-                TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic address space:"
-                                   << intrinsic->address_space;
-        }
-    }
-
-    if (auto* wave_intrinsic =
-            ast::GetAttribute<ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic>(
-                func->Declaration()->attributes)) {
-        switch (wave_intrinsic->op) {
-            case ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Op::kWaveGetLaneCount:
-                out << "WaveGetLaneCount()";
-                return true;
-            case ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Op::kWaveGetLaneIndex:
-                out << "WaveGetLaneIndex()";
-                return true;
-        }
-    }
-
-    out << func->Declaration()->name->symbol.Name() << "(";
-
-    bool first = true;
-    for (auto* arg : call->Arguments()) {
-        if (!first) {
-            out << ", ";
-        }
-        first = false;
-
-        if (!EmitExpression(out, arg->Declaration())) {
-            return false;
-        }
-    }
-
-    out << ")";
-    return true;
-}
-
-bool ASTPrinter::EmitBuiltinCall(StringStream& out,
-                                 const sem::Call* call,
-                                 const sem::BuiltinFn* builtin) {
-    const auto type = builtin->Fn();
-
-    auto* expr = call->Declaration();
-    if (builtin->IsTexture()) {
-        return EmitTextureCall(out, call, builtin);
-    }
-    if (type == wgsl::BuiltinFn::kBitcast) {
-        return EmitBitcastCall(out, expr);
-    }
-    if (type == wgsl::BuiltinFn::kSelect) {
-        return EmitSelectCall(out, expr);
-    }
-    if (type == wgsl::BuiltinFn::kModf) {
-        return EmitModfCall(out, expr, builtin);
-    }
-    if (type == wgsl::BuiltinFn::kFrexp) {
-        return EmitFrexpCall(out, expr, builtin);
-    }
-    if (type == wgsl::BuiltinFn::kDegrees) {
-        return EmitDegreesCall(out, expr, builtin);
-    }
-    if (type == wgsl::BuiltinFn::kRadians) {
-        return EmitRadiansCall(out, expr, builtin);
-    }
-    if (type == wgsl::BuiltinFn::kSign) {
-        return EmitSignCall(out, call, builtin);
-    }
-    if (type == wgsl::BuiltinFn::kQuantizeToF16) {
-        return EmitQuantizeToF16Call(out, expr, builtin);
-    }
-    if (type == wgsl::BuiltinFn::kTrunc) {
-        return EmitTruncCall(out, expr, builtin);
-    }
-    if (builtin->IsDataPacking()) {
-        return EmitDataPackingCall(out, expr, builtin);
-    }
-    if (builtin->IsDataUnpacking()) {
-        return EmitDataUnpackingCall(out, expr, builtin);
-    }
-    if (builtin->IsBarrier()) {
-        return EmitBarrierCall(out, builtin);
-    }
-    if (builtin->IsAtomic()) {
-        return EmitWorkgroupAtomicCall(out, expr, builtin);
-    }
-    if (builtin->IsPacked4x8IntegerDotProductBuiltin()) {
-        return EmitPacked4x8IntegerDotProductBuiltinCall(out, expr, builtin);
-    }
-    if (type == wgsl::BuiltinFn::kSubgroupShuffleXor ||
-        type == wgsl::BuiltinFn::kSubgroupShuffleUp ||
-        type == wgsl::BuiltinFn::kSubgroupShuffleDown) {
-        return EmitSubgroupShuffleBuiltinCall(out, expr, builtin);
-    }
-
-    if (type == wgsl::BuiltinFn::kSubgroupInclusiveAdd ||
-        type == wgsl::BuiltinFn::kSubgroupInclusiveMul) {
-        return EmitSubgroupInclusiveBuiltinCall(out, expr, builtin);
-    }
-    auto name = generate_builtin_name(builtin);
-    if (name.empty()) {
-        return false;
-    }
-
-    // Handle single argument builtins that only accept and return uint (not int overload).
-    // For count bits and reverse bits, we need to explicitly cast the return value (we also cast
-    // the arg for good measure). See crbug.com/tint/1550.
-    // For the following subgroup builtins, the lack of support may be a bug in DXC. See
-    // github.com/microsoft/DirectXShaderCompiler/issues/6850.
-    if (type == wgsl::BuiltinFn::kCountOneBits || type == wgsl::BuiltinFn::kReverseBits ||
-        type == wgsl::BuiltinFn::kSubgroupAnd || type == wgsl::BuiltinFn::kSubgroupOr ||
-        type == wgsl::BuiltinFn::kSubgroupXor) {
-        auto* arg = call->Arguments()[0];
-        auto* argType = arg->Type()->UnwrapRef();
-        if (argType->IsSignedIntegerScalarOrVector()) {
-            // Bitcast of literal int vectors fails in DXC so extract arg to a var. See
-            // github.com/microsoft/DirectXShaderCompiler/issues/6851.
-            if (argType->IsSignedIntegerVector() &&
-                arg->Stage() == core::EvaluationStage::kConstant) {
-                auto varName = UniqueIdentifier(kTempNamePrefix);
-                auto pre = Line();
-                if (!EmitTypeAndName(pre, argType, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, varName)) {
-                    return false;
-                }
-                pre << " = ";
-                if (!EmitExpression(pre, arg->Declaration())) {
-                    return false;
-                }
-                pre << ";";
-                out << "asint(" << name << "(asuint(" << varName << ")))";
-                return true;
-            }
-
-            out << "asint(" << name << "(asuint(";
-            if (!EmitExpression(out, arg->Declaration())) {
-                return false;
-            }
-            out << ")))";
-            return true;
-        }
-    }
-
-    out << name << "(";
-
-    bool first = true;
-    for (auto* arg : call->Arguments()) {
-        if (!first) {
-            out << ", ";
-        }
-        first = false;
-
-        if (!EmitExpression(out, arg->Declaration())) {
-            return false;
-        }
-    }
-
-    out << ")";
-
-    return true;
-}
-
-bool ASTPrinter::EmitValueConversion(StringStream& out,
-                                     const sem::Call* call,
-                                     const sem::ValueConversion* conv) {
-    if (!EmitType(out, conv->Target(), core::AddressSpace::kUndefined, core::Access::kReadWrite,
-                  "")) {
-        return false;
-    }
-    out << "(";
-
-    if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
-        return false;
-    }
-
-    out << ")";
-    return true;
-}
-
-bool ASTPrinter::EmitValueConstructor(StringStream& out,
-                                      const sem::Call* call,
-                                      const sem::ValueConstructor* ctor) {
-    auto* type = call->Type();
-
-    // If the value constructor arguments are empty then we need to construct with the zero value
-    // for all components.
-    if (call->Arguments().IsEmpty()) {
-        return EmitZeroValue(out, type);
-    }
-
-    // Single parameter matrix initializers must be identity initializer.
-    // It could also be conversions between f16 and f32 matrix when f16 is properly supported.
-    if (type->Is<core::type::Matrix>() && call->Arguments().Length() == 1) {
-        if (!ctor->Parameters()[0]->Type()->UnwrapRef()->IsFloatMatrix()) {
-            TINT_UNREACHABLE()
-                << "found a single-parameter matrix initializer that is not identity initializer";
-        }
-    }
-
-    bool brackets = type->IsAnyOf<core::type::Array, core::type::Struct>();
-
-    // For single-value vector initializers, swizzle the scalar to the right
-    // vector dimension using .x
-    const bool is_single_value_vector_init =
-        type->Is<core::type::Vector>() && call->Arguments().Length() == 1 &&
-        ctor->Parameters()[0]->Type()->Is<core::type::Scalar>();
-
-    if (brackets) {
-        out << "{";
-    } else {
-        if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
-            return false;
-        }
-        out << "(";
-    }
-
-    if (is_single_value_vector_init) {
-        out << "(";
-    }
-
-    bool first = true;
-    for (auto* e : call->Arguments()) {
-        if (!first) {
-            out << ", ";
-        }
-        first = false;
-
-        if (!EmitExpression(out, e->Declaration())) {
-            return false;
-        }
-    }
-
-    if (is_single_value_vector_init) {
-        out << ")." << std::string(type->As<core::type::Vector>()->Width(), 'x');
-    }
-
-    out << (brackets ? "}" : ")");
-    return true;
-}
-
-bool ASTPrinter::EmitUniformBufferAccess(StringStream& out,
-                                         const ast::CallExpression* expr,
-                                         const DecomposeMemoryAccess::Intrinsic* intrinsic) {
-    auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
-    auto* const offset = expr->args[0];
-
-    // offset in bytes
-    uint32_t scalar_offset_bytes = 0;
-    // offset in uint (4 bytes)
-    uint32_t scalar_offset_index = 0;
-    // expression to calculate offset in bytes
-    std::string scalar_offset_bytes_expr;
-    // expression to calculate offset in uint, by dividing scalar_offset_bytes_expr by 4
-    std::string scalar_offset_index_expr;
-    // expression to calculate offset in uint, independently
-    std::string scalar_offset_index_unified_expr;
-
-    // If true, use scalar_offset_index, otherwise use scalar_offset_index_expr
-    bool scalar_offset_constant = false;
-
-    if (auto* val = builder_.Sem().GetVal(offset)->ConstantValue()) {
-        TINT_ASSERT(val->Type()->Is<core::type::U32>());
-        scalar_offset_bytes = static_cast<uint32_t>(val->ValueAs<AInt>());
-        scalar_offset_index = scalar_offset_bytes / 4;  // bytes -> scalar index
-        scalar_offset_constant = true;
-    }
-
-    // If true, scalar_offset_bytes or scalar_offset_bytes_expr should be used, otherwise only use
-    // scalar_offset_index or scalar_offset_index_unified_expr. Currently only loading f16 scalar
-    // require using offset in bytes.
-    const bool need_offset_in_bytes =
-        intrinsic->type == DecomposeMemoryAccess::Intrinsic::DataType::kF16;
-
-    if (!scalar_offset_constant) {
-        // UBO offset not compile-time known.
-        // Calculate the scalar offset into a temporary.
-        if (need_offset_in_bytes) {
-            scalar_offset_bytes_expr = UniqueIdentifier("scalar_offset_bytes");
-            scalar_offset_index_expr = UniqueIdentifier("scalar_offset_index");
-            {
-                auto pre = Line();
-                pre << "const uint " << scalar_offset_bytes_expr << " = (";
-                if (!EmitExpression(pre, offset)) {
-                    return false;
-                }
-                pre << ");";
-            }
-            Line() << "const uint " << scalar_offset_index_expr << " = " << scalar_offset_bytes_expr
-                   << " / 4;";
-        } else {
-            scalar_offset_index_unified_expr = UniqueIdentifier("scalar_offset");
-            auto pre = Line();
-            pre << "const uint " << scalar_offset_index_unified_expr << " = (";
-            if (!EmitExpression(pre, offset)) {
-                return false;
-            }
-            pre << ") / 4;";
-        }
-    }
-
-    using Op = DecomposeMemoryAccess::Intrinsic::Op;
-    using DataType = DecomposeMemoryAccess::Intrinsic::DataType;
-    switch (intrinsic->op) {
-        case Op::kLoad: {
-            auto cast = [&](const char* to, auto&& load) {
-                out << to << "(";
-                auto result = load();
-                out << ")";
-                return result;
-            };
-            auto load_u32_to = [&](StringStream& target) {
-                target << buffer;
-                if (scalar_offset_constant) {
-                    target << "[" << (scalar_offset_index / 4) << "]."
-                           << kSwizzle[scalar_offset_index & 3];
-                } else {
-                    target << "[" << scalar_offset_index_unified_expr << " / 4]["
-                           << scalar_offset_index_unified_expr << " % 4]";
-                }
-                return true;
-            };
-            auto load_u32 = [&] { return load_u32_to(out); };
-            // Has a minimum alignment of 8 bytes, so is either .xy or .zw
-            auto load_vec2_u32_to = [&](StringStream& target) {
-                if (scalar_offset_constant) {
-                    target << buffer << "[" << (scalar_offset_index / 4) << "]"
-                           << ((scalar_offset_index & 2) == 0 ? ".xy" : ".zw");
-                } else {
-                    std::string ubo_load = UniqueIdentifier("ubo_load");
-                    {
-                        auto pre = Line();
-                        pre << "uint4 " << ubo_load << " = " << buffer << "["
-                            << scalar_offset_index_unified_expr << " / 4];";
-                    }
-                    target << "((" << scalar_offset_index_unified_expr << " & 2) ? " << ubo_load
-                           << ".zw : " << ubo_load << ".xy)";
-                }
-                return true;
-            };
-            auto load_vec2_u32 = [&] { return load_vec2_u32_to(out); };
-            // vec4 has a minimum alignment of 16 bytes, easiest case
-            auto load_vec4_u32 = [&] {
-                out << buffer;
-                if (scalar_offset_constant) {
-                    out << "[" << (scalar_offset_index / 4) << "]";
-                } else {
-                    out << "[" << scalar_offset_index_unified_expr << " / 4]";
-                }
-                return true;
-            };
-            // vec3 has a minimum alignment of 16 bytes, so is just a .xyz swizzle
-            auto load_vec3_u32 = [&] {
-                if (!load_vec4_u32()) {
-                    return false;
-                }
-                out << ".xyz";
-                return true;
-            };
-            auto load_scalar_f16 = [&] {
-                // offset bytes = 4k,   ((buffer[index].x) & 0xFFFF)
-                // offset bytes = 4k+2, ((buffer[index].x >> 16) & 0xFFFF)
-                out << "float16_t(f16tof32(((" << buffer;
-                if (scalar_offset_constant) {
-                    out << "[" << (scalar_offset_index / 4) << "]."
-                        << kSwizzle[scalar_offset_index & 3];
-                    // WGSL spec ensure little endian memory layout.
-                    if (scalar_offset_bytes % 4 == 0) {
-                        out << ") & 0xFFFF)";
-                    } else {
-                        out << " >> 16) & 0xFFFF)";
-                    }
-                } else {
-                    out << "[" << scalar_offset_index_expr << " / 4][" << scalar_offset_index_expr
-                        << " % 4] >> (" << scalar_offset_bytes_expr
-                        << " % 4 == 0 ? 0 : 16)) & 0xFFFF)";
-                }
-                out << "))";
-                return true;
-            };
-            auto load_vec2_f16 = [&] {
-                // vec2<f16> is aligned to 4 bytes
-                // Preclude code load the vec2<f16> data as a uint:
-                //     uint ubo_load = buffer[id0][id1];
-                // Loading code convert it to vec2<f16>:
-                //     vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)),
-                //     float16_t(f16tof32(ubo_load >> 16)))
-                std::string ubo_load = UniqueIdentifier("ubo_load");
-                {
-                    auto pre = Line();
-                    // Load the 4 bytes f16 vector as an uint
-                    pre << "uint " << ubo_load << " = ";
-                    if (!load_u32_to(pre)) {
-                        return false;
-                    }
-                    pre << ";";
-                }
-                out << "vector<float16_t, 2>(float16_t(f16tof32(" << ubo_load
-                    << " & 0xFFFF)), float16_t(f16tof32(" << ubo_load << " >> 16)))";
-                return true;
-            };
-            auto load_vec3_f16 = [&] {
-                // vec3<f16> is aligned to 8 bytes
-                // Preclude code load the vec3<f16> data as uint2 and convert its elements to
-                // float16_t:
-                //     uint2 ubo_load = buffer[id0].xy;
-                //     /* The low 8 bits of two uint are the x and z elements of vec3<f16> */
-                //     vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
-                //     0xFFFF));
-                //     /* The high 8 bits of first uint is the y element of vec3<f16> */
-                //     float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-                // Loading code convert it to vec3<f16>:
-                //     vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1])
-                std::string ubo_load = UniqueIdentifier("ubo_load");
-                std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
-                std::string ubo_load_y = UniqueIdentifier(ubo_load + "_y");
-                {
-                    auto pre = Line();
-                    // Load the 8 bytes uint2 with the f16 vector at lower 6 bytes
-                    pre << "uint2 " << ubo_load << " = ";
-                    if (!load_vec2_u32_to(pre)) {
-                        return false;
-                    }
-                    pre << ";";
-                }
-                {
-                    auto pre = Line();
-                    pre << "vector<float16_t, 2> " << ubo_load_xz
-                        << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
-                }
-                {
-                    auto pre = Line();
-                    pre << "float16_t " << ubo_load_y << " = f16tof32(" << ubo_load
-                        << "[0] >> 16);";
-                }
-                out << "vector<float16_t, 3>(" << ubo_load_xz << "[0], " << ubo_load_y << ", "
-                    << ubo_load_xz << "[1])";
-                return true;
-            };
-            auto load_vec4_f16 = [&] {
-                // vec4<f16> is aligned to 8 bytes
-                // Preclude code load the vec4<f16> data as uint2 and convert its elements to
-                // float16_t:
-                //     uint2 ubo_load = buffer[id0].xy;
-                //     /* The low 8 bits of two uint are the x and z elements of vec4<f16> */
-                //     vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
-                //     0xFFFF));
-                //     /* The high 8 bits of two uint are the y and w elements of vec4<f16> */
-                //     vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >>
-                //     16));
-                // Loading code convert it to vec4<f16>:
-                //     vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1],
-                //     ubo_load_yw[1])
-                std::string ubo_load = UniqueIdentifier("ubo_load");
-                std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
-                std::string ubo_load_yw = UniqueIdentifier(ubo_load + "_yw");
-                {
-                    auto pre = Line();
-                    // Load the 8 bytes f16 vector as an uint2
-                    pre << "uint2 " << ubo_load << " = ";
-                    if (!load_vec2_u32_to(pre)) {
-                        return false;
-                    }
-                    pre << ";";
-                }
-                {
-                    auto pre = Line();
-                    pre << "vector<float16_t, 2> " << ubo_load_xz
-                        << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
-                }
-                {
-                    auto pre = Line();
-                    pre << "vector<float16_t, 2> " << ubo_load_yw
-                        << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " >> 16));";
-                }
-                out << "vector<float16_t, 4>(" << ubo_load_xz << "[0], " << ubo_load_yw << "[0], "
-                    << ubo_load_xz << "[1], " << ubo_load_yw << "[1])";
-                return true;
-            };
-            switch (intrinsic->type) {
-                case DataType::kU32:
-                    return load_u32();
-                case DataType::kF32:
-                    return cast("asfloat", load_u32);
-                case DataType::kI32:
-                    return cast("asint", load_u32);
-                case DataType::kF16:
-                    return load_scalar_f16();
-                case DataType::kVec2U32:
-                    return load_vec2_u32();
-                case DataType::kVec2F32:
-                    return cast("asfloat", load_vec2_u32);
-                case DataType::kVec2I32:
-                    return cast("asint", load_vec2_u32);
-                case DataType::kVec2F16:
-                    return load_vec2_f16();
-                case DataType::kVec3U32:
-                    return load_vec3_u32();
-                case DataType::kVec3F32:
-                    return cast("asfloat", load_vec3_u32);
-                case DataType::kVec3I32:
-                    return cast("asint", load_vec3_u32);
-                case DataType::kVec3F16:
-                    return load_vec3_f16();
-                case DataType::kVec4U32:
-                    return load_vec4_u32();
-                case DataType::kVec4F32:
-                    return cast("asfloat", load_vec4_u32);
-                case DataType::kVec4I32:
-                    return cast("asint", load_vec4_u32);
-                case DataType::kVec4F16:
-                    return load_vec4_f16();
-            }
-            TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
-                               << static_cast<int>(intrinsic->type);
-        }
-        default:
-            break;
-    }
-    TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
-                       << static_cast<int>(intrinsic->op);
-}
-
-bool ASTPrinter::EmitStorageBufferAccess(StringStream& out,
-                                         const ast::CallExpression* expr,
-                                         const DecomposeMemoryAccess::Intrinsic* intrinsic) {
-    auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
-    auto* const offset = expr->args[0];
-    auto* const value = expr->args.Length() > 1 ? expr->args[1] : nullptr;
-
-    using Op = DecomposeMemoryAccess::Intrinsic::Op;
-    using DataType = DecomposeMemoryAccess::Intrinsic::DataType;
-    switch (intrinsic->op) {
-        case Op::kLoad: {
-            auto load = [&](const char* cast, int n) {
-                if (cast) {
-                    out << cast << "(";
-                }
-                out << buffer << ".Load";
-                if (n > 1) {
-                    out << n;
-                }
-                ScopedParen sp(out);
-                if (!EmitExpression(out, offset)) {
-                    return false;
-                }
-                if (cast) {
-                    out << ")";
-                }
-                return true;
-            };
-            // Templated load used for f16 types, requires SM6.2 or higher and DXC
-            // Used by loading f16 types, e.g. for f16 type, set type parameter to "float16_t"
-            // to emit `buffer.Load<float16_t>(offset)`.
-            auto templated_load = [&](const char* type) {
-                out << buffer << ".Load<" << type << ">";  // templated load
-                ScopedParen sp(out);
-                if (!EmitExpression(out, offset)) {
-                    return false;
-                }
-                return true;
-            };
-            switch (intrinsic->type) {
-                case DataType::kU32:
-                    return load(nullptr, 1);
-                case DataType::kF32:
-                    return load("asfloat", 1);
-                case DataType::kI32:
-                    return load("asint", 1);
-                case DataType::kF16:
-                    return templated_load("float16_t");
-                case DataType::kVec2U32:
-                    return load(nullptr, 2);
-                case DataType::kVec2F32:
-                    return load("asfloat", 2);
-                case DataType::kVec2I32:
-                    return load("asint", 2);
-                case DataType::kVec2F16:
-                    return templated_load("vector<float16_t, 2> ");
-                case DataType::kVec3U32:
-                    return load(nullptr, 3);
-                case DataType::kVec3F32:
-                    return load("asfloat", 3);
-                case DataType::kVec3I32:
-                    return load("asint", 3);
-                case DataType::kVec3F16:
-                    return templated_load("vector<float16_t, 3> ");
-                case DataType::kVec4U32:
-                    return load(nullptr, 4);
-                case DataType::kVec4F32:
-                    return load("asfloat", 4);
-                case DataType::kVec4I32:
-                    return load("asint", 4);
-                case DataType::kVec4F16:
-                    return templated_load("vector<float16_t, 4> ");
-            }
-            TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
-                               << static_cast<int>(intrinsic->type);
-        }
-
-        case Op::kStore: {
-            auto store = [&](int n) {
-                out << buffer << ".Store";
-                if (n > 1) {
-                    out << n;
-                }
-                ScopedParen sp1(out);
-                if (!EmitExpression(out, offset)) {
-                    return false;
-                }
-                out << ", asuint";
-                ScopedParen sp2(out);
-                if (!EmitTextureOrStorageBufferCallArgExpression(out, value)) {
-                    return false;
-                }
-                return true;
-            };
-            // Templated stored used for f16 types, requires SM6.2 or higher and DXC
-            // Used by storing f16 types, e.g. for f16 type, set type parameter to "float16_t"
-            // to emit `buffer.Store<float16_t>(offset)`.
-            auto templated_store = [&](const char* type) {
-                out << buffer << ".Store<" << type << ">";  // templated store
-                ScopedParen sp1(out);
-                if (!EmitExpression(out, offset)) {
-                    return false;
-                }
-                out << ", ";
-                if (!EmitExpression(out, value)) {
-                    return false;
-                }
-                return true;
-            };
-            switch (intrinsic->type) {
-                case DataType::kU32:
-                    return store(1);
-                case DataType::kF32:
-                    return store(1);
-                case DataType::kI32:
-                    return store(1);
-                case DataType::kF16:
-                    return templated_store("float16_t");
-                case DataType::kVec2U32:
-                    return store(2);
-                case DataType::kVec2F32:
-                    return store(2);
-                case DataType::kVec2I32:
-                    return store(2);
-                case DataType::kVec2F16:
-                    return templated_store("vector<float16_t, 2> ");
-                case DataType::kVec3U32:
-                    return store(3);
-                case DataType::kVec3F32:
-                    return store(3);
-                case DataType::kVec3I32:
-                    return store(3);
-                case DataType::kVec3F16:
-                    return templated_store("vector<float16_t, 3> ");
-                case DataType::kVec4U32:
-                    return store(4);
-                case DataType::kVec4F32:
-                    return store(4);
-                case DataType::kVec4I32:
-                    return store(4);
-                case DataType::kVec4F16:
-                    return templated_store("vector<float16_t, 4> ");
-            }
-            TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
-                               << static_cast<int>(intrinsic->type);
-        }
-        default:
-            // Break out to error case below
-            // Note that atomic intrinsics are generated as functions.
-            break;
-    }
-
-    TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
-                       << static_cast<int>(intrinsic->op);
-}
-
-bool ASTPrinter::EmitStorageAtomicIntrinsic(const ast::Function* func,
-                                            const DecomposeMemoryAccess::Intrinsic* intrinsic) {
-    using Op = DecomposeMemoryAccess::Intrinsic::Op;
-
-    const sem::Function* sem_func = builder_.Sem().Get(func);
-    auto* result_ty = sem_func->ReturnType();
-    const auto name = func->name->symbol.Name();
-    auto& buf = *current_buffer_;
-
-    auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
-
-    auto rmw = [&](const char* hlsl) -> bool {
-        {
-            auto fn = Line(&buf);
-            if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
-                                 core::Access::kUndefined, name)) {
-                return false;
-            }
-            fn << "(uint offset, ";
-            if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
-                                 core::Access::kUndefined, "value")) {
-                return false;
-            }
-            fn << ") {";
-        }
-
-        buf.IncrementIndent();
-        TINT_DEFER({
-            buf.DecrementIndent();
-            Line(&buf) << "}";
-            Line(&buf);
-        });
-
-        {
-            auto l = Line(&buf);
-            if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
-                                 core::Access::kUndefined, "original_value")) {
-                return false;
-            }
-            l << " = 0;";
-        }
-        {
-            auto l = Line(&buf);
-            l << buffer << "." << hlsl << "(offset, ";
-            if (intrinsic->op == Op::kAtomicSub) {
-                l << "-";
-            }
-            l << "value, original_value);";
-        }
-        Line(&buf) << "return original_value;";
-        return true;
-    };
-
-    switch (intrinsic->op) {
-        case Op::kAtomicAdd:
-            return rmw("InterlockedAdd");
-
-        case Op::kAtomicSub:
-            // Use add with the operand negated.
-            return rmw("InterlockedAdd");
-
-        case Op::kAtomicMax:
-            return rmw("InterlockedMax");
-
-        case Op::kAtomicMin:
-            return rmw("InterlockedMin");
-
-        case Op::kAtomicAnd:
-            return rmw("InterlockedAnd");
-
-        case Op::kAtomicOr:
-            return rmw("InterlockedOr");
-
-        case Op::kAtomicXor:
-            return rmw("InterlockedXor");
-
-        case Op::kAtomicExchange:
-            return rmw("InterlockedExchange");
-
-        case Op::kAtomicLoad: {
-            // HLSL does not have an InterlockedLoad, so we emulate it with
-            // InterlockedOr using 0 as the OR value
-            {
-                auto fn = Line(&buf);
-                if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, name)) {
-                    return false;
-                }
-                fn << "(uint offset) {";
-            }
-
-            buf.IncrementIndent();
-            TINT_DEFER({
-                buf.DecrementIndent();
-                Line(&buf) << "}";
-                Line(&buf);
-            });
-
-            {
-                auto l = Line(&buf);
-                if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, "value")) {
-                    return false;
-                }
-                l << " = 0;";
-            }
-
-            Line(&buf) << buffer << ".InterlockedOr(offset, 0, value);";
-            Line(&buf) << "return value;";
-            return true;
-        }
-        case Op::kAtomicStore: {
-            auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
-            // HLSL does not have an InterlockedStore, so we emulate it with
-            // InterlockedExchange and discard the returned value
-            {
-                auto fn = Line(&buf);
-                fn << "void " << name << "(uint offset, ";
-                if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, "value")) {
-                    return false;
-                }
-                fn << ") {";
-            }
-
-            buf.IncrementIndent();
-            TINT_DEFER({
-                buf.DecrementIndent();
-                Line(&buf) << "}";
-                Line(&buf);
-            });
-
-            {
-                auto l = Line(&buf);
-                if (!EmitTypeAndName(l, value_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, "ignored")) {
-                    return false;
-                }
-                l << ";";
-            }
-            Line(&buf) << buffer << ".InterlockedExchange(offset, value, ignored);";
-            return true;
-        }
-        case Op::kAtomicCompareExchangeWeak: {
-            if (!EmitStructType(&helpers_, result_ty->As<core::type::Struct>())) {
-                return false;
-            }
-
-            auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
-            // NOTE: We don't need to emit the return type struct here as DecomposeMemoryAccess
-            // already added it to the AST, and it should have already been emitted by now.
-            {
-                auto fn = Line(&buf);
-                if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, name)) {
-                    return false;
-                }
-                fn << "(uint offset, ";
-                if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, "compare")) {
-                    return false;
-                }
-                fn << ", ";
-                if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, "value")) {
-                    return false;
-                }
-                fn << ") {";
-            }
-
-            buf.IncrementIndent();
-            TINT_DEFER({
-                buf.DecrementIndent();
-                Line(&buf) << "}";
-                Line(&buf);
-            });
-
-            {  // T result = {0};
-                auto l = Line(&buf);
-                if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, "result")) {
-                    return false;
-                }
-                l << "=";
-                if (!EmitZeroValue(l, result_ty)) {
-                    return false;
-                }
-                l << ";";
-            }
-
-            Line(&buf) << buffer
-                       << ".InterlockedCompareExchange(offset, compare, value, result.old_value);";
-            Line(&buf) << "result.exchanged = result.old_value == compare;";
-            Line(&buf) << "return result;";
-
-            return true;
-        }
-        default:
-            break;
-    }
-
-    TINT_UNREACHABLE() << "unsupported atomic DecomposeMemoryAccess::Intrinsic::Op: "
-                       << static_cast<int>(intrinsic->op);
-}
-
-bool ASTPrinter::EmitWorkgroupAtomicCall(StringStream& out,
-                                         const ast::CallExpression* expr,
-                                         const sem::BuiltinFn* builtin) {
-    std::string result = UniqueIdentifier("atomic_result");
-
-    if (!builtin->ReturnType()->Is<core::type::Void>()) {
-        auto pre = Line();
-        if (!EmitTypeAndName(pre, builtin->ReturnType(), core::AddressSpace::kUndefined,
-                             core::Access::kUndefined, result)) {
-            return false;
-        }
-        pre << " = ";
-        if (!EmitZeroValue(pre, builtin->ReturnType())) {
-            return false;
-        }
-        pre << ";";
-    }
-
-    auto call = [&](const char* name) {
-        auto pre = Line();
-        pre << name;
-
-        {
-            ScopedParen sp(pre);
-            for (size_t i = 0; i < expr->args.Length(); i++) {
-                auto* arg = expr->args[i];
-                if (i > 0) {
-                    pre << ", ";
-                }
-                if (!EmitExpression(pre, arg)) {
-                    return false;
-                }
-            }
-
-            pre << ", " << result;
-        }
-
-        pre << ";";
-
-        out << result;
-        return true;
-    };
-
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kAtomicLoad: {
-            // HLSL does not have an InterlockedLoad, so we emulate it with
-            // InterlockedOr using 0 as the OR value
-            auto pre = Line();
-            pre << "InterlockedOr";
-            {
-                ScopedParen sp(pre);
-                if (!EmitExpression(pre, expr->args[0])) {
-                    return false;
-                }
-                pre << ", 0, " << result;
-            }
-            pre << ";";
-
-            out << result;
-            return true;
-        }
-        case wgsl::BuiltinFn::kAtomicStore: {
-            // HLSL does not have an InterlockedStore, so we emulate it with
-            // InterlockedExchange and discard the returned value
-            {  // T result = 0;
-                auto pre = Line();
-                auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
-                if (!EmitTypeAndName(pre, value_ty, core::AddressSpace::kUndefined,
-                                     core::Access::kUndefined, result)) {
-                    return false;
-                }
-                pre << " = ";
-                if (!EmitZeroValue(pre, value_ty)) {
-                    return false;
-                }
-                pre << ";";
-            }
-
-            out << "InterlockedExchange";
-            {
-                ScopedParen sp(out);
-                if (!EmitExpression(out, expr->args[0])) {
-                    return false;
-                }
-                out << ", ";
-                if (!EmitExpression(out, expr->args[1])) {
-                    return false;
-                }
-                out << ", " << result;
-            }
-            return true;
-        }
-        case wgsl::BuiltinFn::kAtomicCompareExchangeWeak: {
-            if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
-                return false;
-            }
-
-            auto* dest = expr->args[0];
-            auto* compare_value = expr->args[1];
-            auto* value = expr->args[2];
-
-            std::string compare = UniqueIdentifier("atomic_compare_value");
-
-            {  // T compare_value = <compare_value>;
-                auto pre = Line();
-                if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
-                                     core::AddressSpace::kUndefined, core::Access::kUndefined,
-                                     compare)) {
-                    return false;
-                }
-                pre << " = ";
-                if (!EmitExpression(pre, compare_value)) {
-                    return false;
-                }
-                pre << ";";
-            }
-
-            {  // InterlockedCompareExchange(dst, compare, value, result.old_value);
-                auto pre = Line();
-                pre << "InterlockedCompareExchange";
-                {
-                    ScopedParen sp(pre);
-                    if (!EmitExpression(pre, dest)) {
-                        return false;
-                    }
-                    pre << ", " << compare << ", ";
-                    if (!EmitExpression(pre, value)) {
-                        return false;
-                    }
-                    pre << ", " << result << ".old_value";
-                }
-                pre << ";";
-            }
-
-            // result.exchanged = result.old_value == compare;
-            Line() << result << ".exchanged = " << result << ".old_value == " << compare << ";";
-
-            out << result;
-            return true;
-        }
-
-        case wgsl::BuiltinFn::kAtomicAdd:
-            return call("InterlockedAdd");
-
-        case wgsl::BuiltinFn::kAtomicSub: {
-            auto pre = Line();
-            // Sub uses InterlockedAdd with the operand negated.
-            pre << "InterlockedAdd";
-            {
-                ScopedParen sp(pre);
-                TINT_ASSERT(expr->args.Length() == 2);
-
-                if (!EmitExpression(pre, expr->args[0])) {
-                    return false;
-                }
-                pre << ", -";
-                {
-                    ScopedParen argSP(pre);
-                    if (!EmitExpression(pre, expr->args[1])) {
-                        return false;
-                    }
-                }
-
-                pre << ", " << result;
-            }
-
-            pre << ";";
-
-            out << result;
-        }
-            return true;
-
-        case wgsl::BuiltinFn::kAtomicMax:
-            return call("InterlockedMax");
-
-        case wgsl::BuiltinFn::kAtomicMin:
-            return call("InterlockedMin");
-
-        case wgsl::BuiltinFn::kAtomicAnd:
-            return call("InterlockedAnd");
-
-        case wgsl::BuiltinFn::kAtomicOr:
-            return call("InterlockedOr");
-
-        case wgsl::BuiltinFn::kAtomicXor:
-            return call("InterlockedXor");
-
-        case wgsl::BuiltinFn::kAtomicExchange:
-            return call("InterlockedExchange");
-
-        default:
-            break;
-    }
-
-    TINT_UNREACHABLE() << "unsupported atomic builtin: " << builtin->Fn();
-}
-
-bool ASTPrinter::EmitSelectCall(StringStream& out, const ast::CallExpression* expr) {
-    auto* expr_false = expr->args[0];
-    auto* expr_true = expr->args[1];
-    auto* expr_cond = expr->args[2];
-    ScopedParen paren(out);
-    if (!EmitExpression(out, expr_cond)) {
-        return false;
-    }
-
-    out << " ? ";
-
-    if (!EmitExpression(out, expr_true)) {
-        return false;
-    }
-
-    out << " : ";
-
-    if (!EmitExpression(out, expr_false)) {
-        return false;
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitModfCall(StringStream& out,
-                              const ast::CallExpression* expr,
-                              const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(
-        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
-            auto* ty = builtin->Parameters()[0]->Type();
-            auto in = params[0];
-
-            std::string width;
-            if (auto* vec = ty->As<core::type::Vector>()) {
-                width = std::to_string(vec->Width());
-            }
-
-            // Emit the builtin return type unique to this overload. This does not
-            // exist in the AST, so it will not be generated in Generate().
-            if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
-                return false;
-            }
-
-            {
-                auto l = Line(b);
-                if (!EmitType(l, builtin->ReturnType(), core::AddressSpace::kUndefined,
-                              core::Access::kUndefined, "")) {
-                    return false;
-                }
-                l << " result;";
-            }
-            Line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
-            Line(b) << "return result;";
-            return true;
-        });
-}
-
-bool ASTPrinter::EmitFrexpCall(StringStream& out,
-                               const ast::CallExpression* expr,
-                               const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(
-        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
-            auto* ty = builtin->Parameters()[0]->Type();
-            auto in = params[0];
-
-            std::string width;
-            if (auto* vec = ty->As<core::type::Vector>()) {
-                width = std::to_string(vec->Width());
-            }
-
-            // Emit the builtin return type unique to this overload. This does not
-            // exist in the AST, so it will not be generated in Generate().
-            if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
-                return false;
-            }
-
-            std::string member_type;
-            if (Is<core::type::F16>(ty->DeepestElement())) {
-                member_type = width.empty() ? "float16_t" : ("vector<float16_t, " + width + ">");
-            } else {
-                member_type = "float" + width;
-            }
-
-            Line(b) << member_type << " exp;";
-            Line(b) << member_type << " fract = sign(" << in << ") * frexp(" << in << ", exp);";
-            {
-                auto l = Line(b);
-                if (!EmitType(l, builtin->ReturnType(), core::AddressSpace::kUndefined,
-                              core::Access::kUndefined, "")) {
-                    return false;
-                }
-                l << " result = {fract, int" << width << "(exp)};";
-            }
-            Line(b) << "return result;";
-            return true;
-        });
-}
-
-bool ASTPrinter::EmitDegreesCall(StringStream& out,
-                                 const ast::CallExpression* expr,
-                                 const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(out, expr, builtin,
-                             [&](TextBuffer* b, const std::vector<std::string>& params) {
-                                 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
-                                         << sem::kRadToDeg << ";";
-                                 return true;
-                             });
-}
-
-bool ASTPrinter::EmitRadiansCall(StringStream& out,
-                                 const ast::CallExpression* expr,
-                                 const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(out, expr, builtin,
-                             [&](TextBuffer* b, const std::vector<std::string>& params) {
-                                 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
-                                         << sem::kDegToRad << ";";
-                                 return true;
-                             });
-}
-
-// The HLSL `sign` method always returns an `int` result (scalar or vector). In WGSL the result is
-// expected to be the same type as the argument. This injects a cast to the expected WGSL result
-// type after the call to `sign`.
-bool ASTPrinter::EmitSignCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn*) {
-    auto* arg = call->Arguments()[0];
-    if (!EmitType(out, arg->Type(), core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
-        return false;
-    }
-    out << "(sign(";
-    if (!EmitExpression(out, arg->Declaration())) {
-        return false;
-    }
-    out << "))";
-    return true;
-}
-
-bool ASTPrinter::EmitQuantizeToF16Call(StringStream& out,
-                                       const ast::CallExpression* expr,
-                                       const sem::BuiltinFn* builtin) {
-    // Cast to f16 and back
-    std::string width;
-    if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
-        width = std::to_string(vec->Width());
-    }
-    out << "f16tof32(f32tof16(";
-    if (!EmitExpression(out, expr->args[0])) {
-        return false;
-    }
-    out << "))";
-    return true;
-}
-
-bool ASTPrinter::EmitTruncCall(StringStream& out,
-                               const ast::CallExpression* expr,
-                               const sem::BuiltinFn* builtin) {
-    // HLSL's trunc is broken for very large/small float values.
-    // See crbug.com/tint/1883
-    return CallBuiltinHelper(  //
-        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
-            // value < 0 ? ceil(value) : floor(value)
-            Line(b) << "return " << params[0] << " < 0 ? ceil(" << params[0] << ") : floor("
-                    << params[0] << ");";
-            return true;
-        });
-}
-
-bool ASTPrinter::EmitDataPackingCall(StringStream& out,
-                                     const ast::CallExpression* expr,
-                                     const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(
-        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
-            uint32_t dims = 2;
-            bool is_signed = false;
-            uint32_t scale = 65535;
-            if (builtin->Fn() == wgsl::BuiltinFn::kPack4X8Snorm ||
-                builtin->Fn() == wgsl::BuiltinFn::kPack4X8Unorm) {
-                dims = 4;
-                scale = 255;
-            }
-            if (builtin->Fn() == wgsl::BuiltinFn::kPack4X8Snorm ||
-                builtin->Fn() == wgsl::BuiltinFn::kPack2X16Snorm) {
-                is_signed = true;
-                scale = (scale - 1) / 2;
-            }
-            switch (builtin->Fn()) {
-                case wgsl::BuiltinFn::kPack4X8Snorm:
-                case wgsl::BuiltinFn::kPack4X8Unorm:
-                case wgsl::BuiltinFn::kPack2X16Snorm:
-                case wgsl::BuiltinFn::kPack2X16Unorm: {
-                    {
-                        auto l = Line(b);
-                        l << (is_signed ? "" : "u") << "int" << dims
-                          << " i = " << (is_signed ? "" : "u") << "int" << dims << "(round(clamp("
-                          << params[0] << ", " << (is_signed ? "-1.0" : "0.0") << ", 1.0) * "
-                          << scale << ".0))";
-                        if (is_signed) {
-                            l << " & " << (dims == 4 ? "0xff" : "0xffff");
-                        }
-                        l << ";";
-                    }
-                    {
-                        auto l = Line(b);
-                        l << "return ";
-                        if (is_signed) {
-                            l << "asuint";
-                        }
-                        l << "(i.x | i.y << " << (32 / dims);
-                        if (dims == 4) {
-                            l << " | i.z << 16 | i.w << 24";
-                        }
-                        l << ");";
-                    }
-                    break;
-                }
-                case wgsl::BuiltinFn::kPack2X16Float: {
-                    Line(b) << "uint2 i = f32tof16(" << params[0] << ");";
-                    Line(b) << "return i.x | (i.y << 16);";
-                    break;
-                }
-                default:
-                    TINT_ICE() << " unhandled data packing builtin";
-            }
-
-            return true;
-        });
-}
-
-bool ASTPrinter::EmitDataUnpackingCall(StringStream& out,
-                                       const ast::CallExpression* expr,
-                                       const sem::BuiltinFn* builtin) {
-    return CallBuiltinHelper(
-        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
-            uint32_t dims = 2;
-            bool is_signed = false;
-            uint32_t scale = 65535;
-            if (builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Snorm ||
-                builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Unorm) {
-                dims = 4;
-                scale = 255;
-            }
-            if (builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Snorm ||
-                builtin->Fn() == wgsl::BuiltinFn::kUnpack2X16Snorm) {
-                is_signed = true;
-                scale = (scale - 1) / 2;
-            }
-            switch (builtin->Fn()) {
-                case wgsl::BuiltinFn::kUnpack4X8Snorm:
-                case wgsl::BuiltinFn::kUnpack2X16Snorm: {
-                    Line(b) << "int j = int(" << params[0] << ");";
-                    {  // Perform sign extension on the converted values.
-                        auto l = Line(b);
-                        l << "int" << dims << " i = int" << dims << "(";
-                        if (dims == 2) {
-                            l << "j << 16, j) >> 16";
-                        } else {
-                            l << "j << 24, j << 16, j << 8, j) >> 24";
-                        }
-                        l << ";";
-                    }
-                    Line(b) << "return clamp(float" << dims << "(i) / " << scale << ".0, "
-                            << (is_signed ? "-1.0" : "0.0") << ", 1.0);";
-                    break;
-                }
-                case wgsl::BuiltinFn::kUnpack4X8Unorm:
-                case wgsl::BuiltinFn::kUnpack2X16Unorm: {
-                    Line(b) << "uint j = " << params[0] << ";";
-                    {
-                        auto l = Line(b);
-                        l << "uint" << dims << " i = uint" << dims << "(";
-                        l << "j & " << (dims == 2 ? "0xffff" : "0xff") << ", ";
-                        if (dims == 4) {
-                            l << "(j >> " << (32 / dims) << ") & 0xff, (j >> 16) & 0xff, j >> 24";
-                        } else {
-                            l << "j >> " << (32 / dims);
-                        }
-                        l << ");";
-                    }
-                    Line(b) << "return float" << dims << "(i) / " << scale << ".0;";
-                    break;
-                }
-                case wgsl::BuiltinFn::kUnpack2X16Float:
-                    Line(b) << "uint i = " << params[0] << ";";
-                    Line(b) << "return f16tof32(uint2(i & 0xffff, i >> 16));";
-                    break;
-                default:
-                    TINT_ICE() << "unhandled data packing builtin";
-            }
-
-            return true;
-        });
-}
-
-bool ASTPrinter::EmitPacked4x8IntegerDotProductBuiltinCall(StringStream& out,
-                                                           const ast::CallExpression* expr,
-                                                           const sem::BuiltinFn* builtin) {
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kDot4I8Packed:
-        case wgsl::BuiltinFn::kDot4U8Packed:
-            break;
-        case wgsl::BuiltinFn::kPack4XI8: {
-            out << "uint(pack_s8(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << "))";
-            return true;
-        }
-        case wgsl::BuiltinFn::kPack4XU8: {
-            out << "uint(pack_u8(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << "))";
-            return true;
-        }
-        case wgsl::BuiltinFn::kPack4XI8Clamp: {
-            out << "uint(pack_clamp_s8(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << "))";
-            return true;
-        }
-        case wgsl::BuiltinFn::kUnpack4XI8: {
-            out << "unpack_s8s32(int8_t4_packed(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << "))";
-            return true;
-        }
-        case wgsl::BuiltinFn::kUnpack4XU8: {
-            out << "unpack_u8u32(uint8_t4_packed(";
-            if (!EmitExpression(out, expr->args[0])) {
-                return false;
-            }
-            out << "))";
-            return true;
-        }
-        case wgsl::BuiltinFn::kPack4XU8Clamp:
-        default:
-            TINT_UNIMPLEMENTED() << builtin->Fn();
-    }
-
-    return CallBuiltinHelper(out, expr, builtin,
-                             [&](TextBuffer* b, const std::vector<std::string>& params) {
-                                 std::string functionName;
-                                 switch (builtin->Fn()) {
-                                     case wgsl::BuiltinFn::kDot4I8Packed:
-                                         Line(b) << "int accumulator = 0;";
-                                         functionName = "dot4add_i8packed";
-                                         break;
-                                     case wgsl::BuiltinFn::kDot4U8Packed:
-                                         Line(b) << "uint accumulator = 0u;";
-                                         functionName = "dot4add_u8packed";
-                                         break;
-                                     default:
-                                         TINT_ICE() << "Internal error: unhandled DP4a builtin";
-                                 }
-                                 Line(b) << "return " << functionName << "(" << params[0] << ", "
-                                         << params[1] << ", accumulator);";
-
-                                 return true;
-                             });
-}
-
-bool ASTPrinter::EmitBarrierCall(StringStream& out, const sem::BuiltinFn* builtin) {
-    // TODO(crbug.com/tint/661): Combine sequential barriers to a single
-    // instruction.
-    if (builtin->Fn() == wgsl::BuiltinFn::kWorkgroupBarrier) {
-        out << "GroupMemoryBarrierWithGroupSync()";
-    } else if (builtin->Fn() == wgsl::BuiltinFn::kStorageBarrier) {
-        out << "DeviceMemoryBarrierWithGroupSync()";
-    } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureBarrier) {
-        out << "DeviceMemoryBarrierWithGroupSync()";
-    } else {
-        TINT_UNREACHABLE() << "unexpected barrier builtin type " << builtin->Fn();
-    }
-    return true;
-}
-
-bool ASTPrinter::EmitTextureOrStorageBufferCallArgExpression(StringStream& out,
-                                                             const ast::Expression* expr) {
-    // TODO(crbug.com/tint/1976): Workaround DXC bug that fails to compile texture/storage function
-    // calls with signed integer splatted constants. DXC fails to convert the coord arg, for e.g.
-    // `0.xxx`, from a vector of 64-bit ints to a vector of 32-bit ints to match the texture load
-    // parameter type. We work around this for now by explicitly casting the splatted constant to
-    // the right type, for e.g. `int3(0.xxx)`.
-    bool emitted_cast = false;
-    if (auto* sem = builder_.Sem().GetVal(expr)) {
-        if (auto* constant = sem->ConstantValue()) {
-            if (auto* splat = constant->As<core::constant::Splat>()) {
-                if (splat->Type()->IsSignedIntegerVector()) {
-                    if (!EmitType(out, constant->Type(), core::AddressSpace::kUndefined,
-                                  core::Access::kUndefined, "")) {
-                        return false;
-                    }
-                    out << "(";
-                    emitted_cast = true;
-                }
-            }
-        }
-    }
-    if (!EmitExpression(out, expr)) {
-        return false;
-    }
-    if (emitted_cast) {
-        out << ")";
-    }
-    return true;
-}
-
-bool ASTPrinter::EmitTextureCall(StringStream& out,
-                                 const sem::Call* call,
-                                 const sem::BuiltinFn* builtin) {
-    using Usage = core::ParameterUsage;
-
-    auto& signature = builtin->Signature();
-    auto* expr = call->Declaration();
-    auto arguments = expr->args;
-
-    // Returns the argument with the given usage
-    auto arg = [&](Usage usage) {
-        int idx = signature.IndexOf(usage);
-        return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
-    };
-
-    auto* texture = arg(Usage::kTexture);
-    if (DAWN_UNLIKELY(!texture)) {
-        TINT_ICE() << "missing texture argument";
-    }
-
-    auto* texture_type = TypeOf(texture)->UnwrapRef()->As<core::type::Texture>();
-
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kTextureDimensions:
-        case wgsl::BuiltinFn::kTextureNumLayers:
-        case wgsl::BuiltinFn::kTextureNumLevels:
-        case wgsl::BuiltinFn::kTextureNumSamples: {
-            // All of these builtins use the GetDimensions() method on the texture
-            bool is_ms = texture_type->IsAnyOf<core::type::MultisampledTexture,
-                                               core::type::DepthMultisampledTexture>();
-            unsigned int num_dimensions = 0;
-            std::string swizzle;
-
-            switch (builtin->Fn()) {
-                case wgsl::BuiltinFn::kTextureDimensions:
-                    switch (texture_type->Dim()) {
-                        case core::type::TextureDimension::kNone:
-                            TINT_ICE() << "texture dimension is kNone";
-                        case core::type::TextureDimension::k1d:
-                            num_dimensions = 1;
-                            break;
-                        case core::type::TextureDimension::k2d:
-                            num_dimensions = is_ms ? 3 : 2;
-                            swizzle = is_ms ? ".xy" : "";
-                            break;
-                        case core::type::TextureDimension::k2dArray:
-                            num_dimensions = is_ms ? 4 : 3;
-                            swizzle = ".xy";
-                            break;
-                        case core::type::TextureDimension::k3d:
-                            num_dimensions = 3;
-                            break;
-                        case core::type::TextureDimension::kCube:
-                            num_dimensions = 2;
-                            break;
-                        case core::type::TextureDimension::kCubeArray:
-                            num_dimensions = 3;
-                            swizzle = ".xy";
-                            break;
-                    }
-                    break;
-                case wgsl::BuiltinFn::kTextureNumLayers:
-                    switch (texture_type->Dim()) {
-                        default:
-                            TINT_ICE() << "texture dimension is not arrayed";
-                        case core::type::TextureDimension::k2dArray:
-                            num_dimensions = is_ms ? 4 : 3;
-                            swizzle = ".z";
-                            break;
-                        case core::type::TextureDimension::kCubeArray:
-                            num_dimensions = 3;
-                            swizzle = ".z";
-                            break;
-                    }
-                    break;
-                case wgsl::BuiltinFn::kTextureNumLevels:
-                    switch (texture_type->Dim()) {
-                        default:
-                            TINT_ICE() << "texture dimension does not support mips";
-                        case core::type::TextureDimension::k1d:
-                            num_dimensions = 2;
-                            swizzle = ".y";
-                            break;
-                        case core::type::TextureDimension::k2d:
-                        case core::type::TextureDimension::kCube:
-                            num_dimensions = 3;
-                            swizzle = ".z";
-                            break;
-                        case core::type::TextureDimension::k2dArray:
-                        case core::type::TextureDimension::k3d:
-                        case core::type::TextureDimension::kCubeArray:
-                            num_dimensions = 4;
-                            swizzle = ".w";
-                            break;
-                    }
-                    break;
-                case wgsl::BuiltinFn::kTextureNumSamples:
-                    switch (texture_type->Dim()) {
-                        default:
-                            TINT_ICE() << "texture dimension does not support multisampling";
-                        case core::type::TextureDimension::k2d:
-                            num_dimensions = 3;
-                            swizzle = ".z";
-                            break;
-                        case core::type::TextureDimension::k2dArray:
-                            num_dimensions = 4;
-                            swizzle = ".w";
-                            break;
-                    }
-                    break;
-                default:
-                    TINT_ICE() << "unexpected builtin";
-            }
-
-            auto* level_arg = arg(Usage::kLevel);
-
-            if (level_arg) {
-                // `NumberOfLevels` is a non-optional argument if `MipLevel` was passed.
-                // Increment the number of dimensions for the temporary vector to
-                // accommodate this.
-                num_dimensions++;
-
-                // If the swizzle was empty, the expression will evaluate to the whole
-                // vector. As we've grown the vector by one element, we now need to
-                // swizzle to keep the result expression equivalent.
-                if (swizzle.empty()) {
-                    static constexpr std::array<const char*, 4> swizzles = {"", ".x", ".xy",
-                                                                            ".xyz"};
-                    swizzle = swizzles[num_dimensions - 1];
-                }
-            }
-
-            if (DAWN_UNLIKELY(num_dimensions > 4)) {
-                TINT_ICE() << "Texture query builtin temporary vector has " << num_dimensions
-                           << " dimensions";
-            }
-
-            // Declare a variable to hold the queried texture info
-            auto dims = UniqueIdentifier(kTempNamePrefix);
-            if (num_dimensions == 1) {
-                Line() << "uint " << dims << ";";
-            } else {
-                Line() << "uint" << num_dimensions << " " << dims << ";";
-            }
-
-            {  // texture.GetDimensions(...)
-                auto pre = Line();
-                if (!EmitExpression(pre, texture)) {
-                    return false;
-                }
-                pre << ".GetDimensions(";
-
-                if (level_arg) {
-                    if (!EmitExpression(pre, level_arg)) {
-                        return false;
-                    }
-                    pre << ", ";
-                } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureNumLevels) {
-                    pre << "0, ";
-                }
-
-                if (num_dimensions == 1) {
-                    pre << dims;
-                } else {
-                    if (DAWN_UNLIKELY(num_dimensions < 0 || num_dimensions > 4)) {
-                        TINT_ICE() << "vector dimensions are " << num_dimensions;
-                    }
-                    for (unsigned int i = 0; i < num_dimensions; i++) {
-                        if (i > 0) {
-                            pre << ", ";
-                        }
-                        pre << dims << "." << kSwizzle[i];
-                    }
-                }
-
-                pre << ");";
-            }
-
-            // The out parameters of the GetDimensions() call is now in temporary
-            // `dims` variable. This may be packed with other data, so the final
-            // expression may require a swizzle.
-            out << dims << swizzle;
-            return true;
-        }
-        default:
-            break;
-    }
-
-    if (!EmitExpression(out, texture)) {
-        return false;
-    }
-
-    // If pack_level_in_coords is true, then the mip level will be appended as the
-    // last value of the coordinates argument. If the WGSL builtin overload does
-    // not have a level parameter and pack_level_in_coords is true, then a zero
-    // mip level will be inserted.
-    bool pack_level_in_coords = false;
-
-    uint32_t hlsl_ret_width = 4u;
-
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kTextureSample:
-            out << ".Sample(";
-            break;
-        case wgsl::BuiltinFn::kTextureSampleBias:
-            out << ".SampleBias(";
-            break;
-        case wgsl::BuiltinFn::kTextureSampleLevel:
-            out << ".SampleLevel(";
-            break;
-        case wgsl::BuiltinFn::kTextureSampleGrad:
-            out << ".SampleGrad(";
-            break;
-        case wgsl::BuiltinFn::kTextureSampleCompare:
-            out << ".SampleCmp(";
-            hlsl_ret_width = 1;
-            break;
-        case wgsl::BuiltinFn::kTextureSampleCompareLevel:
-            out << ".SampleCmpLevelZero(";
-            hlsl_ret_width = 1;
-            break;
-        case wgsl::BuiltinFn::kTextureLoad:
-            out << ".Load(";
-            // Multisampled textures and read-write storage textures do not support mip-levels.
-            if (texture_type->Is<core::type::MultisampledTexture>()) {
-                break;
-            }
-            if (auto* storage_texture_type = texture_type->As<core::type::StorageTexture>()) {
-                if (storage_texture_type->Access() == core::Access::kReadWrite) {
-                    break;
-                }
-            }
-            pack_level_in_coords = true;
-            break;
-        case wgsl::BuiltinFn::kTextureGather:
-            out << ".Gather";
-            if (builtin->Parameters()[0]->Usage() == core::ParameterUsage::kComponent) {
-                switch (call->Arguments()[0]->ConstantValue()->ValueAs<AInt>()) {
-                    case 0:
-                        out << "Red";
-                        break;
-                    case 1:
-                        out << "Green";
-                        break;
-                    case 2:
-                        out << "Blue";
-                        break;
-                    case 3:
-                        out << "Alpha";
-                        break;
-                }
-            }
-            out << "(";
-            break;
-        case wgsl::BuiltinFn::kTextureGatherCompare:
-            out << ".GatherCmp(";
-            break;
-        case wgsl::BuiltinFn::kTextureStore:
-            out << "[";
-            break;
-        default:
-            TINT_ICE() << "Unhandled texture builtin '" << builtin << "'";
-    }
-
-    if (auto* sampler = arg(Usage::kSampler)) {
-        if (!EmitExpression(out, sampler)) {
-            return false;
-        }
-        out << ", ";
-    }
-
-    auto* param_coords = arg(Usage::kCoords);
-    if (DAWN_UNLIKELY(!param_coords)) {
-        TINT_ICE() << "missing coords argument";
-    }
-
-    auto emit_vector_appended_with_i32_zero = [&](const ast::Expression* vector) {
-        auto* i32 = builder_.create<core::type::I32>();
-        auto* zero = builder_.Expr(0_i);
-        auto* stmt = builder_.Sem().Get(vector)->Stmt();
-        builder_.Sem().Add(zero, builder_.create<sem::ValueExpression>(
-                                     zero, i32, core::EvaluationStage::kRuntime, stmt,
-                                     /* constant_value */ nullptr,
-                                     /* has_side_effects */ false));
-        auto* packed = AppendVector(&builder_, vector, zero);
-        return EmitExpression(out, packed->Declaration());
-    };
-
-    auto emit_vector_appended_with_level = [&](const ast::Expression* vector) {
-        if (auto* level = arg(Usage::kLevel)) {
-            auto* packed = AppendVector(&builder_, vector, level);
-            return EmitExpression(out, packed->Declaration());
-        }
-        return emit_vector_appended_with_i32_zero(vector);
-    };
-
-    if (auto* array_index = arg(Usage::kArrayIndex)) {
-        // Array index needs to be appended to the coordinates.
-        auto* packed = AppendVector(&builder_, param_coords, array_index);
-        if (pack_level_in_coords) {
-            // Then mip level needs to be appended to the coordinates.
-            if (!emit_vector_appended_with_level(packed->Declaration())) {
-                return false;
-            }
-        } else {
-            if (!EmitExpression(out, packed->Declaration())) {
-                return false;
-            }
-        }
-    } else if (pack_level_in_coords) {
-        // Mip level needs to be appended to the coordinates.
-        if (!emit_vector_appended_with_level(param_coords)) {
-            return false;
-        }
-    } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
-        // param_coords is an index expression, not a function arg
-        if (!EmitExpression(out, param_coords)) {
-            return false;
-        }
-    } else if (!EmitTextureOrStorageBufferCallArgExpression(out, param_coords)) {
-        return false;
-    }
-
-    for (auto usage : {Usage::kDepthRef, Usage::kBias, Usage::kLevel, Usage::kDdx, Usage::kDdy,
-                       Usage::kSampleIndex, Usage::kOffset}) {
-        if (usage == Usage::kLevel && pack_level_in_coords) {
-            continue;  // mip level already packed in coordinates.
-        }
-        if (auto* e = arg(usage)) {
-            out << ", ";
-            if (usage == Usage::kBias) {
-                out << "clamp(";
-            }
-            if (!EmitTextureOrStorageBufferCallArgExpression(out, e)) {
-                return false;
-            }
-            if (usage == Usage::kBias) {
-                out << ", -16.0f, 15.99f)";
-            }
-        }
-    }
-
-    if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
-        out << "] = ";
-        if (!EmitExpression(out, arg(Usage::kValue))) {
-            return false;
-        }
-    } else {
-        out << ")";
-
-        // If the builtin return type does not match the number of elements of the
-        // HLSL builtin, we need to swizzle the expression to generate the correct
-        // number of components.
-        uint32_t wgsl_ret_width = 1;
-        if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
-            wgsl_ret_width = vec->Width();
-        }
-        if (wgsl_ret_width < hlsl_ret_width) {
-            out << ".";
-            TINT_ASSERT(wgsl_ret_width < 3);
-            for (uint32_t i = 0; i < wgsl_ret_width; i++) {
-                out << kSwizzle[i];
-            }
-        }
-        if (DAWN_UNLIKELY(wgsl_ret_width > hlsl_ret_width)) {
-            TINT_ICE() << "WGSL return width (" << wgsl_ret_width
-                       << ") is wider than HLSL return width (" << hlsl_ret_width << ") for "
-                       << builtin->Fn();
-        }
-    }
-
-    return true;
-}
-
-// The following subgroup builtin functions are translated to HLSL as follows:
-// +---------------------+----------------------------------------------------------------+
-// |        WGSL         |                              HLSL                              |
-// +---------------------+----------------------------------------------------------------+
-// | subgroupShuffleXor  | WaveReadLaneAt with index equal subgroup_invocation_id ^ mask  |
-// | subgroupShuffleUp   | WaveReadLaneAt with index equal subgroup_invocation_id - delta |
-// | subgroupShuffleDown | WaveReadLaneAt with index equal subgroup_invocation_id + delta |
-// +---------------------+----------------------------------------------------------------+
-bool ASTPrinter::EmitSubgroupShuffleBuiltinCall(StringStream& out,
-                                                const ast::CallExpression* expr,
-                                                const sem::BuiltinFn* builtin) {
-    out << "WaveReadLaneAt(";
-
-    if (!EmitExpression(out, expr->args[0])) {
-        return false;
-    }
-
-    out << ", (WaveGetLaneIndex() ";
-
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kSubgroupShuffleXor:
-            out << "^ ";
-            break;
-        case wgsl::BuiltinFn::kSubgroupShuffleUp:
-            out << "- ";
-            break;
-        case wgsl::BuiltinFn::kSubgroupShuffleDown:
-            out << "+ ";
-            break;
-        default:
-            TINT_UNREACHABLE();
-    }
-
-    if (!EmitExpression(out, expr->args[1])) {
-        return false;
-    }
-    out << "))";
-
-    return true;
-}
-
-// The following subgroup builtin functions are translated to HLSL as follows:
-// +-----------------------+----------------------+
-// |        WGSL           |       HLSL           |
-// +-----------------------+----------------------+
-// | subgroupInclusiveAdd  | WavePrefixSum(x) + x |
-// | subgroupInclusiveMul  | WavePrefixMul(x) * x |
-// +-----------------------+----------------------+
-bool ASTPrinter::EmitSubgroupInclusiveBuiltinCall(StringStream& out,
-                                                  const ast::CallExpression* expr,
-                                                  const sem::BuiltinFn* builtin) {
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kSubgroupInclusiveAdd:
-            out << "(WavePrefixSum(";
-            break;
-        case wgsl::BuiltinFn::kSubgroupInclusiveMul:
-            out << "(WavePrefixProduct(";
-            break;
-        default:
-            TINT_UNREACHABLE();
-    }
-
-    if (!EmitExpression(out, expr->args[0])) {
-        return false;
-    }
-
-    out << ") ";
-
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kSubgroupInclusiveAdd:
-            out << "+";
-            break;
-        case wgsl::BuiltinFn::kSubgroupInclusiveMul:
-            out << "*";
-            break;
-        default:
-            TINT_UNREACHABLE();
-    }
-    // Add a space after the operand to be more consistent with IR generated code.
-    out << " ";
-    if (!EmitExpression(out, expr->args[0])) {
-        return false;
-    }
-
-    out << ")";
-
-    return true;
-}
-
-std::string ASTPrinter::generate_builtin_name(const sem::BuiltinFn* builtin) {
-    switch (builtin->Fn()) {
-        case wgsl::BuiltinFn::kAbs:
-        case wgsl::BuiltinFn::kAcos:
-        case wgsl::BuiltinFn::kAll:
-        case wgsl::BuiltinFn::kAny:
-        case wgsl::BuiltinFn::kAsin:
-        case wgsl::BuiltinFn::kAtan:
-        case wgsl::BuiltinFn::kAtan2:
-        case wgsl::BuiltinFn::kCeil:
-        case wgsl::BuiltinFn::kClamp:
-        case wgsl::BuiltinFn::kCos:
-        case wgsl::BuiltinFn::kCosh:
-        case wgsl::BuiltinFn::kCross:
-        case wgsl::BuiltinFn::kDeterminant:
-        case wgsl::BuiltinFn::kDistance:
-        case wgsl::BuiltinFn::kDot:
-        case wgsl::BuiltinFn::kExp:
-        case wgsl::BuiltinFn::kExp2:
-        case wgsl::BuiltinFn::kFloor:
-        case wgsl::BuiltinFn::kFrexp:
-        case wgsl::BuiltinFn::kLdexp:
-        case wgsl::BuiltinFn::kLength:
-        case wgsl::BuiltinFn::kLog:
-        case wgsl::BuiltinFn::kLog2:
-        case wgsl::BuiltinFn::kMax:
-        case wgsl::BuiltinFn::kMin:
-        case wgsl::BuiltinFn::kModf:
-        case wgsl::BuiltinFn::kNormalize:
-        case wgsl::BuiltinFn::kPow:
-        case wgsl::BuiltinFn::kReflect:
-        case wgsl::BuiltinFn::kRefract:
-        case wgsl::BuiltinFn::kRound:
-        case wgsl::BuiltinFn::kSaturate:
-        case wgsl::BuiltinFn::kSin:
-        case wgsl::BuiltinFn::kSinh:
-        case wgsl::BuiltinFn::kSqrt:
-        case wgsl::BuiltinFn::kStep:
-        case wgsl::BuiltinFn::kTan:
-        case wgsl::BuiltinFn::kTanh:
-        case wgsl::BuiltinFn::kTranspose:
-            return builtin->str();
-        case wgsl::BuiltinFn::kCountOneBits:  // uint
-            return "countbits";
-        case wgsl::BuiltinFn::kDpdx:
-            return "ddx";
-        case wgsl::BuiltinFn::kDpdxCoarse:
-            return "ddx_coarse";
-        case wgsl::BuiltinFn::kDpdxFine:
-            return "ddx_fine";
-        case wgsl::BuiltinFn::kDpdy:
-            return "ddy";
-        case wgsl::BuiltinFn::kDpdyCoarse:
-            return "ddy_coarse";
-        case wgsl::BuiltinFn::kDpdyFine:
-            return "ddy_fine";
-        case wgsl::BuiltinFn::kFaceForward:
-            return "faceforward";
-        case wgsl::BuiltinFn::kFract:
-            return "frac";
-        case wgsl::BuiltinFn::kFma:
-            return "mad";
-        case wgsl::BuiltinFn::kFwidth:
-        case wgsl::BuiltinFn::kFwidthCoarse:
-        case wgsl::BuiltinFn::kFwidthFine:
-            return "fwidth";
-        case wgsl::BuiltinFn::kInverseSqrt:
-            return "rsqrt";
-        case wgsl::BuiltinFn::kMix:
-            return "lerp";
-        case wgsl::BuiltinFn::kReverseBits:  // uint
-            return "reversebits";
-        case wgsl::BuiltinFn::kSmoothstep:
-            return "smoothstep";
-        case wgsl::BuiltinFn::kSubgroupBallot:
-            return "WaveActiveBallot";
-        case wgsl::BuiltinFn::kSubgroupElect:
-            return "WaveIsFirstLane";
-        case wgsl::BuiltinFn::kSubgroupBroadcast:
-            return "WaveReadLaneAt";
-        case wgsl::BuiltinFn::kSubgroupBroadcastFirst:
-            return "WaveReadLaneFirst";
-        case wgsl::BuiltinFn::kSubgroupShuffle:
-            return "WaveReadLaneAt";
-        case wgsl::BuiltinFn::kSubgroupAdd:
-            return "WaveActiveSum";
-        case wgsl::BuiltinFn::kSubgroupExclusiveAdd:
-            return "WavePrefixSum";
-        case wgsl::BuiltinFn::kSubgroupMul:
-            return "WaveActiveProduct";
-        case wgsl::BuiltinFn::kSubgroupExclusiveMul:
-            return "WavePrefixProduct";
-        case wgsl::BuiltinFn::kSubgroupAnd:
-            return "WaveActiveBitAnd";
-        case wgsl::BuiltinFn::kSubgroupOr:
-            return "WaveActiveBitOr";
-        case wgsl::BuiltinFn::kSubgroupXor:
-            return "WaveActiveBitXor";
-        case wgsl::BuiltinFn::kSubgroupMin:
-            return "WaveActiveMin";
-        case wgsl::BuiltinFn::kSubgroupMax:
-            return "WaveActiveMax";
-        case wgsl::BuiltinFn::kSubgroupAll:
-            return "WaveActiveAllTrue";
-        case wgsl::BuiltinFn::kSubgroupAny:
-            return "WaveActiveAnyTrue";
-        case wgsl::BuiltinFn::kQuadBroadcast:
-            return "QuadReadLaneAt";
-        case wgsl::BuiltinFn::kQuadSwapX:
-            return "QuadReadAcrossX";
-        case wgsl::BuiltinFn::kQuadSwapY:
-            return "QuadReadAcrossY";
-        case wgsl::BuiltinFn::kQuadSwapDiagonal:
-            return "QuadReadAcrossDiagonal";
-        default:
-            diagnostics_.AddError(Source{}) << "Unknown builtin method: " << builtin->str();
-    }
-
-    return "";
-}
-
-bool ASTPrinter::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
-    auto* stmt = s->body[case_idx];
-    auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
-    for (auto* selector : sem->Selectors()) {
-        auto out = Line();
-        if (selector->IsDefault()) {
-            out << "default";
-        } else {
-            out << "case ";
-            if (!EmitConstant(out, selector->Value(), /* is_variable_initializer */ false)) {
-                return false;
-            }
-        }
-        out << ":";
-        if (selector == sem->Selectors().back()) {
-            out << " {";
-        }
-    }
-
-    IncrementIndent();
-    TINT_DEFER({
-        DecrementIndent();
-        Line() << "}";
-    });
-
-    // Emit the case statement
-    if (!EmitStatements(stmt->body->statements)) {
-        return false;
-    }
-
-    if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
-        Line() << "break;";
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitContinue(const ast::ContinueStatement*) {
-    if (!emit_continuing_ || !emit_continuing_()) {
-        return false;
-    }
-    Line() << "continue;";
-    return true;
-}
-
-bool ASTPrinter::EmitDiscard(const ast::DiscardStatement*) {
-    // TODO(dsinclair): Verify this is correct when the discard semantics are
-    // defined for WGSL (https://github.com/gpuweb/gpuweb/issues/361)
-    Line() << "discard;";
-    return true;
-}
-
-bool ASTPrinter::EmitExpression(StringStream& out, const ast::Expression* expr) {
-    if (auto* sem = builder_.Sem().GetVal(expr)) {
-        if (auto* constant = sem->ConstantValue()) {
-            bool is_variable_initializer = false;
-            if (auto* stmt = sem->Stmt()) {
-                if (auto* decl = As<ast::VariableDeclStatement>(stmt->Declaration())) {
-                    is_variable_initializer = decl->variable->initializer == expr;
-                }
-            }
-            return EmitConstant(out, constant, is_variable_initializer);
-        }
-    }
-    return Switch(
-        expr,  //
-        [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(out, a); },
-        [&](const ast::BinaryExpression* b) { return EmitBinary(out, b); },
-        [&](const ast::CallExpression* c) { return EmitCall(out, c); },
-        [&](const ast::IdentifierExpression* i) { return EmitIdentifier(out, i); },
-        [&](const ast::LiteralExpression* l) { return EmitLiteral(out, l); },
-        [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(out, m); },
-        [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(out, u); },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitIdentifier(StringStream& out, const ast::IdentifierExpression* expr) {
-    out << expr->identifier->symbol.Name();
-    return true;
-}
-
-bool ASTPrinter::EmitIf(const ast::IfStatement* stmt) {
-    {
-        auto out = Line();
-        out << "if (";
-        if (!EmitExpression(out, stmt->condition)) {
-            return false;
-        }
-        out << ") {";
-    }
-
-    if (!EmitStatementsWithIndent(stmt->body->statements)) {
-        return false;
-    }
-
-    if (stmt->else_statement) {
-        Line() << "} else {";
-        if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
-            if (!EmitStatementsWithIndent(block->statements)) {
-                return false;
-            }
-        } else {
-            if (!EmitStatementsWithIndent(Vector{stmt->else_statement})) {
-                return false;
-            }
-        }
-    }
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitFunction(const ast::Function* func) {
-    auto* sem = builder_.Sem().Get(func);
-
-    // Emit storage atomic helpers
-    if (auto* intrinsic = ast::GetAttribute<DecomposeMemoryAccess::Intrinsic>(func->attributes)) {
-        if (intrinsic->address_space == core::AddressSpace::kStorage && intrinsic->IsAtomic()) {
-            if (!EmitStorageAtomicIntrinsic(func, intrinsic)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    if (ast::HasAttribute<ast::InternalAttribute>(func->attributes)) {
-        // An internal function. Do not emit.
-        return true;
-    }
-
-    {
-        auto out = Line();
-        auto name = func->name->symbol.Name();
-        // If the function returns an array, then we need to declare a typedef for
-        // this.
-        if (sem->ReturnType()->Is<core::type::Array>()) {
-            auto typedef_name = UniqueIdentifier(name + "_ret");
-            auto pre = Line();
-            pre << "typedef ";
-            if (!EmitTypeAndName(pre, sem->ReturnType(), core::AddressSpace::kUndefined,
-                                 core::Access::kReadWrite, typedef_name)) {
-                return false;
-            }
-            pre << ";";
-            out << typedef_name;
-        } else {
-            if (!EmitType(out, sem->ReturnType(), core::AddressSpace::kUndefined,
-                          core::Access::kReadWrite, "")) {
-                return false;
-            }
-        }
-
-        out << " " << name << "(";
-
-        bool first = true;
-
-        for (auto* v : sem->Parameters()) {
-            if (!first) {
-                out << ", ";
-            }
-            first = false;
-
-            auto const* type = v->Type();
-            auto address_space = core::AddressSpace::kUndefined;
-            auto access = core::Access::kUndefined;
-
-            if (auto* ptr = type->As<core::type::Pointer>()) {
-                type = ptr->StoreType();
-                switch (ptr->AddressSpace()) {
-                    case core::AddressSpace::kStorage:
-                    case core::AddressSpace::kUniform:
-                        // Not allowed by WGSL, but is used by certain transforms (e.g. DMA) to pass
-                        // storage buffers and uniform buffers down into transform-generated
-                        // functions. In this situation we want to generate the parameter without an
-                        // 'inout', using the address space and access from the pointer.
-                        address_space = ptr->AddressSpace();
-                        access = ptr->Access();
-                        break;
-                    default:
-                        // Transform regular WGSL pointer parameters in to `inout` parameters.
-                        out << "inout ";
-                }
-            }
-
-            // Note: WGSL only allows for AddressSpace::kUndefined on parameters, however
-            // the sanitizer transforms generates load / store functions for storage
-            // or uniform buffers. These functions have a buffer parameter with
-            // AddressSpace::kStorage or AddressSpace::kUniform. This is required to
-            // correctly translate the parameter to a [RW]ByteAddressBuffer for
-            // storage buffers and a uint4[N] for uniform buffers.
-            if (!EmitTypeAndName(out, type, address_space, access,
-                                 v->Declaration()->name->symbol.Name())) {
-                return false;
-            }
-        }
-        out << ") {";
-    }
-
-    if (sem->DiscardStatement() && !sem->ReturnType()->Is<core::type::Void>()) {
-        // BUG(crbug.com/tint/1081): work around non-void functions with discard
-        // failing compilation sometimes
-        if (!EmitFunctionBodyWithDiscard(func)) {
-            return false;
-        }
-    } else {
-        if (!EmitStatementsWithIndent(func->body->statements)) {
-            return false;
-        }
-    }
-
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitFunctionBodyWithDiscard(const ast::Function* func) {
-    // FXC sometimes fails to compile functions that discard with 'Not all control
-    // paths return a value'. We work around this by wrapping the function body
-    // within an "if (true) { <body> } return <default return type obj>;" so that
-    // there is always an (unused) return statement.
-
-    auto* sem = builder_.Sem().Get(func);
-    TINT_ASSERT(sem->DiscardStatement() && !sem->ReturnType()->Is<core::type::Void>());
-
-    ScopedIndent si(this);
-    Line() << "if (true) {";
-
-    if (!EmitStatementsWithIndent(func->body->statements)) {
-        return false;
-    }
-
-    Line() << "}";
-
-    // Return an unused result that matches the type of the return value
-    auto name = builder_.Symbols().New("unused").Name();
-    {
-        auto out = Line();
-        if (!EmitTypeAndName(out, sem->ReturnType(), core::AddressSpace::kUndefined,
-                             core::Access::kReadWrite, name)) {
-            return false;
-        }
-        out << ";";
-    }
-    Line() << "return " << name << ";";
-
-    return true;
-}
-
-bool ASTPrinter::EmitGlobalVariable(const ast::Variable* global) {
-    return Switch(
-        global,  //
-        [&](const ast::Var* var) {
-            auto* sem = builder_.Sem().Get(global);
-            switch (sem->AddressSpace()) {
-                case core::AddressSpace::kUniform:
-                    return EmitUniformVariable(var, sem);
-                case core::AddressSpace::kStorage:
-                    return EmitStorageVariable(var, sem);
-                case core::AddressSpace::kHandle:
-                    return EmitHandleVariable(var, sem);
-                case core::AddressSpace::kPrivate:
-                    return EmitPrivateVariable(sem);
-                case core::AddressSpace::kWorkgroup:
-                    return EmitWorkgroupVariable(sem);
-                case core::AddressSpace::kImmediate:
-                    diagnostics_.AddError(Source{})
-                        << "unhandled address space " << sem->AddressSpace();
-                    return false;
-                default: {
-                    TINT_ICE() << "unhandled address space " << sem->AddressSpace();
-                }
-            }
-        },
-        [&](const ast::Override*) {
-            // Override is removed with SubstituteOverride
-            diagnostics_.AddError(Source{})
-                << "override-expressions should have been removed with the SubstituteOverride "
-                   "transform";
-            return false;
-        },
-        [&](const ast::Const*) {
-            return true;  // Constants are embedded at their use
-        },                //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitUniformVariable(const ast::Var* var, const sem::Variable* sem) {
-    auto binding_point = *sem->As<sem::GlobalVariable>()->Attributes().binding_point;
-    auto* type = sem->Type()->UnwrapRef();
-    auto name = var->name->symbol.Name();
-    Line() << "cbuffer cbuffer_" << name << RegisterAndSpace('b', binding_point) << " {";
-
-    {
-        ScopedIndent si(this);
-        auto out = Line();
-        if (!EmitTypeAndName(out, type, core::AddressSpace::kUniform, sem->Access(), name)) {
-            return false;
-        }
-        out << ";";
-    }
-
-    Line() << "};";
-
-    return true;
-}
-
-bool ASTPrinter::EmitStorageVariable(const ast::Var* var, const sem::Variable* sem) {
-    auto* type = sem->Type()->UnwrapRef();
-    auto out = Line();
-    if (!EmitTypeAndName(out, type, core::AddressSpace::kStorage, sem->Access(),
-                         var->name->symbol.Name())) {
-        return false;
-    }
-
-    auto* global_sem = sem->As<sem::GlobalVariable>();
-    out << RegisterAndSpace(sem->Access() == core::Access::kRead ? 't' : 'u',
-                            *global_sem->Attributes().binding_point)
-        << ";";
-
-    return true;
-}
-
-bool ASTPrinter::EmitHandleVariable(const ast::Var* var, const sem::Variable* sem) {
-    auto* unwrapped_type = sem->Type()->UnwrapRef();
-    auto out = Line();
-
-    auto name = var->name->symbol.Name();
-    auto* type = sem->Type()->UnwrapRef();
-    if (ast::HasAttribute<PixelLocal::RasterizerOrderedView>(var->attributes)) {
-        TINT_ASSERT(!type->Is<core::type::MultisampledTexture>());
-        auto* storage = type->As<core::type::StorageTexture>();
-        if (!storage) {
-            TINT_ICE() << "Rasterizer Ordered View type isn't storage texture";
-        }
-        out << "RasterizerOrderedTexture2D";
-        auto* component = ImageFormatToRWtextureType(storage->TexelFormat());
-        if (DAWN_UNLIKELY(!component)) {
-            TINT_ICE() << "Unsupported StorageTexture TexelFormat: "
-                       << static_cast<int>(storage->TexelFormat());
-        }
-        out << "<" << component << "> " << name;
-    } else if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), name)) {
-        return false;
-    }
-
-    const char* register_space = nullptr;
-
-    if (unwrapped_type->Is<core::type::Texture>()) {
-        register_space = "t";
-        if (auto* st = unwrapped_type->As<core::type::StorageTexture>();
-            st && st->Access() != core::Access::kRead) {
-            register_space = "u";
-        }
-    } else if (unwrapped_type->Is<core::type::Sampler>()) {
-        register_space = "s";
-    }
-
-    if (register_space) {
-        auto bp = sem->As<sem::GlobalVariable>()->Attributes().binding_point;
-        out << " : register(" << register_space << bp->binding;
-        // Omit the space if it's 0, as it's the default.
-        // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better
-        // compatibility.
-        if (bp->group == 0) {
-            out << ")";
-        } else {
-            out << ", space" << bp->group << ")";
-        }
-    }
-
-    out << ";";
-    return true;
-}
-
-bool ASTPrinter::EmitPrivateVariable(const sem::Variable* var) {
-    auto* decl = var->Declaration();
-    auto out = Line();
-
-    out << "static ";
-
-    auto name = decl->name->symbol.Name();
-    auto* type = var->Type()->UnwrapRef();
-    if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
-        return false;
-    }
-
-    out << " = ";
-    if (auto* initializer = decl->initializer) {
-        if (!EmitExpression(out, initializer)) {
-            return false;
-        }
-    } else {
-        if (!EmitZeroValue(out, var->Type()->UnwrapRef())) {
-            return false;
-        }
-    }
-
-    out << ";";
-    return true;
-}
-
-bool ASTPrinter::EmitWorkgroupVariable(const sem::Variable* var) {
-    auto* decl = var->Declaration();
-    auto out = Line();
-
-    out << "groupshared ";
-
-    auto name = decl->name->symbol.Name();
-    auto* type = var->Type()->UnwrapRef();
-    if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
-        return false;
-    }
-
-    if (auto* initializer = decl->initializer) {
-        out << " = ";
-        if (!EmitExpression(out, initializer)) {
-            return false;
-        }
-    }
-
-    out << ";";
-    return true;
-}
-
-std::string ASTPrinter::builtin_to_attribute(core::BuiltinValue builtin) const {
-    switch (builtin) {
-        case core::BuiltinValue::kPosition:
-            return "SV_Position";
-        case core::BuiltinValue::kVertexIndex:
-            return "SV_VertexID";
-        case core::BuiltinValue::kInstanceIndex:
-            return "SV_InstanceID";
-        case core::BuiltinValue::kFrontFacing:
-            return "SV_IsFrontFace";
-        case core::BuiltinValue::kFragDepth:
-            return "SV_Depth";
-        case core::BuiltinValue::kLocalInvocationId:
-            return "SV_GroupThreadID";
-        case core::BuiltinValue::kLocalInvocationIndex:
-            return "SV_GroupIndex";
-        case core::BuiltinValue::kGlobalInvocationId:
-            return "SV_DispatchThreadID";
-        case core::BuiltinValue::kWorkgroupId:
-            return "SV_GroupID";
-        case core::BuiltinValue::kSampleIndex:
-            return "SV_SampleIndex";
-        case core::BuiltinValue::kSampleMask:
-            return "SV_Coverage";
-        case core::BuiltinValue::kClipDistances:
-            return "SV_ClipDistance0";
-        default:
-            break;
-    }
-    return "";
-}
-
-std::string ASTPrinter::interpolation_to_modifiers(core::InterpolationType type,
-                                                   core::InterpolationSampling sampling) const {
-    std::string modifiers;
-    switch (type) {
-        case core::InterpolationType::kPerspective:
-            modifiers += "linear ";
-            break;
-        case core::InterpolationType::kLinear:
-            modifiers += "noperspective ";
-            break;
-        case core::InterpolationType::kFlat:
-            modifiers += "nointerpolation ";
-            break;
-        case core::InterpolationType::kUndefined:
-            break;
-    }
-    switch (sampling) {
-        case core::InterpolationSampling::kCentroid:
-            modifiers += "centroid ";
-            break;
-        case core::InterpolationSampling::kSample:
-            modifiers += "sample ";
-            break;
-        case core::InterpolationSampling::kCenter:
-        case core::InterpolationSampling::kFirst:
-        case core::InterpolationSampling::kEither:
-        case core::InterpolationSampling::kUndefined:
-            break;
-    }
-    return modifiers;
-}
-
-bool ASTPrinter::EmitEntryPointFunction(const ast::Function* func) {
-    auto* func_sem = builder_.Sem().Get(func);
-
-    {
-        auto out = Line();
-        if (func->PipelineStage() == ast::PipelineStage::kCompute) {
-            // Emit the workgroup_size attribute.
-            auto wgsize = func_sem->WorkgroupSize();
-            out << "[numthreads(";
-            for (size_t i = 0; i < 3; i++) {
-                if (i > 0) {
-                    out << ", ";
-                }
-                if (!wgsize[i].has_value()) {
-                    diagnostics_.AddError(Source{})
-                        << "override-expressions should have been removed with the "
-                           "SubstituteOverride transform";
-                    return false;
-                }
-                out << std::to_string(wgsize[i].value());
-            }
-            out << ")]\n";
-        }
-
-        if (!EmitTypeAndName(out, func_sem->ReturnType(), core::AddressSpace::kUndefined,
-                             core::Access::kUndefined, func->name->symbol.Name())) {
-            return false;
-        }
-        out << "(";
-
-        bool first = true;
-
-        // Emit entry point parameters.
-        for (auto* var : func->params) {
-            auto* sem = builder_.Sem().Get(var);
-            auto* type = sem->Type();
-            if (DAWN_UNLIKELY(!type->Is<core::type::Struct>())) {
-                // ICE likely indicates that the CanonicalizeEntryPointIO transform was
-                // not run, or a builtin parameter was added after it was run.
-                TINT_ICE() << "Unsupported non-struct entry point parameter";
-            }
-
-            if (!first) {
-                out << ", ";
-            }
-            first = false;
-
-            if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
-                                 var->name->symbol.Name())) {
-                return false;
-            }
-        }
-
-        out << ") {";
-    }
-
-    {
-        ScopedIndent si(this);
-
-        if (!EmitStatements(func->body->statements)) {
-            return false;
-        }
-
-        if (!Is<ast::ReturnStatement>(func->body->Last())) {
-            ast::ReturnStatement ret(GenerationID(), ast::NodeID{}, Source{});
-            if (!EmitStatement(&ret)) {
-                return false;
-            }
-        }
-    }
-
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitConstant(StringStream& out,
-                              const core::constant::Value* constant,
-                              bool is_variable_initializer) {
-    return Switch(
-        constant->Type(),  //
-        [&](const core::type::Bool*) {
-            out << (constant->ValueAs<AInt>() ? "true" : "false");
-            return true;
-        },
-        [&](const core::type::F32*) {
-            PrintF32(out, constant->ValueAs<f32>());
-            return true;
-        },
-        [&](const core::type::F16*) {
-            // emit a f16 scalar with explicit float16_t type declaration.
-            out << "float16_t(";
-            PrintF16(out, constant->ValueAs<f16>());
-            out << ")";
-            return true;
-        },
-        [&](const core::type::I32*) {
-            out << constant->ValueAs<AInt>();
-            return true;
-        },
-        [&](const core::type::U32*) {
-            out << constant->ValueAs<AInt>() << "u";
-            return true;
-        },
-        [&](const core::type::Vector* v) {
-            if (auto* splat = constant->As<core::constant::Splat>()) {
-                {
-                    ScopedParen sp(out);
-                    if (!EmitConstant(out, splat->el, is_variable_initializer)) {
-                        return false;
-                    }
-                }
-                out << ".";
-                for (size_t i = 0; i < v->Width(); i++) {
-                    out << "x";
-                }
-                return true;
-            }
-
-            if (!EmitType(out, v, core::AddressSpace::kUndefined, core::Access::kUndefined, "")) {
-                return false;
-            }
-
-            ScopedParen sp(out);
-
-            for (size_t i = 0; i < v->Width(); i++) {
-                if (i > 0) {
-                    out << ", ";
-                }
-                if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
-                    return false;
-                }
-            }
-            return true;
-        },
-        [&](const core::type::Matrix* m) {
-            if (!EmitType(out, m, core::AddressSpace::kUndefined, core::Access::kUndefined, "")) {
-                return false;
-            }
-
-            ScopedParen sp(out);
-
-            for (size_t i = 0; i < m->Columns(); i++) {
-                if (i > 0) {
-                    out << ", ";
-                }
-                if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
-                    return false;
-                }
-            }
-            return true;
-        },
-        [&](const core::type::Array* a) {
-            if (constant->AllZero()) {
-                out << "(";
-                if (!EmitType(out, a, core::AddressSpace::kUndefined, core::Access::kUndefined,
-                              "")) {
-                    return false;
-                }
-                out << ")0";
-                return true;
-            }
-
-            out << "{";
-            TINT_DEFER(out << "}");
-
-            auto count = a->ConstantCount();
-            if (!count) {
-                diagnostics_.AddError(Source{}) << core::type::Array::kErrExpectedConstantCount;
-                return false;
-            }
-
-            for (size_t i = 0; i < count; i++) {
-                if (i > 0) {
-                    out << ", ";
-                }
-                if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
-                    return false;
-                }
-            }
-
-            return true;
-        },
-        [&](const core::type::Struct* s) {
-            if (!EmitStructType(&helpers_, s)) {
-                return false;
-            }
-
-            if (constant->AllZero()) {
-                out << "(" << StructName(s) << ")0";
-                return true;
-            }
-
-            auto emit_member_values = [&](StringStream& o) {
-                o << "{";
-                for (size_t i = 0; i < s->Members().Length(); i++) {
-                    if (i > 0) {
-                        o << ", ";
-                    }
-                    if (!EmitConstant(o, constant->Index(i), is_variable_initializer)) {
-                        return false;
-                    }
-                }
-                o << "}";
-                return true;
-            };
-
-            if (is_variable_initializer) {
-                if (!emit_member_values(out)) {
-                    return false;
-                }
-            } else {
-                // HLSL requires structure initializers to be assigned directly to a variable.
-                // For these constants use 'static const' at global-scope. 'const' at global scope
-                // creates a variable who's initializer is ignored, and the value is expected to be
-                // provided in a cbuffer. 'static const' is a true value-embedded-in-the-shader-code
-                // constant. We also emit these for function-local constant expressions for
-                // consistency and to ensure that these are not computed at execution time.
-                auto name = UniqueIdentifier("c");
-                {
-                    StringStream decl;
-                    decl << "static const " << StructName(s) << " " << name << " = ";
-                    if (!emit_member_values(decl)) {
-                        return false;
-                    }
-                    decl << ";";
-                    current_buffer_->Insert(decl.str(), global_insertion_point_++, 0);
-                }
-                out << name;
-            }
-
-            return true;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitLiteral(StringStream& out, const ast::LiteralExpression* lit) {
-    return Switch(
-        lit,
-        [&](const ast::BoolLiteralExpression* l) {
-            out << (l->value ? "true" : "false");
-            return true;
-        },
-        [&](const ast::FloatLiteralExpression* l) {
-            if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
-                // Emit f16 literal with explicit float16_t type declaration.
-                out << "float16_t(";
-                PrintF16(out, static_cast<float>(l->value));
-                out << ")";
-            }
-            PrintF32(out, static_cast<float>(l->value));
-            return true;
-        },
-        [&](const ast::IntLiteralExpression* i) {
-            out << i->value;
-            switch (i->suffix) {
-                case ast::IntLiteralExpression::Suffix::kNone:
-                case ast::IntLiteralExpression::Suffix::kI:
-                    return true;
-                case ast::IntLiteralExpression::Suffix::kU:
-                    out << "u";
-                    return true;
-            }
-            diagnostics_.AddError(Source{}) << "unknown integer literal suffix type";
-            return false;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitValue(StringStream& out, const core::type::Type* type, int value) {
-    return Switch(
-        type,
-        [&](const core::type::Bool*) {
-            out << (value == 0 ? "false" : "true");
-            return true;
-        },
-        [&](const core::type::F32*) {
-            out << value << ".0f";
-            return true;
-        },
-        [&](const core::type::F16*) {
-            out << "float16_t(" << value << ".0h)";
-            return true;
-        },
-        [&](const core::type::I32*) {
-            out << value;
-            return true;
-        },
-        [&](const core::type::U32*) {
-            out << value << "u";
-            return true;
-        },
-        [&](const core::type::Vector* vec) {
-            if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite,
-                          "")) {
-                return false;
-            }
-            ScopedParen sp(out);
-            for (uint32_t i = 0; i < vec->Width(); i++) {
-                if (i != 0) {
-                    out << ", ";
-                }
-                if (!EmitValue(out, vec->Type(), value)) {
-                    return false;
-                }
-            }
-            return true;
-        },
-        [&](const core::type::Matrix* mat) {
-            if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite,
-                          "")) {
-                return false;
-            }
-            ScopedParen sp(out);
-            for (uint32_t i = 0; i < (mat->Rows() * mat->Columns()); i++) {
-                if (i != 0) {
-                    out << ", ";
-                }
-                if (!EmitValue(out, mat->Type(), value)) {
-                    return false;
-                }
-            }
-            return true;
-        },
-        [&](const core::type::Struct*) {
-            out << "(";
-            TINT_DEFER(out << ")" << value);
-            return EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
-                            "");
-        },
-        [&](const core::type::Array*) {
-            out << "(";
-            TINT_DEFER(out << ")" << value);
-            return EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
-                            "");
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitZeroValue(StringStream& out, const core::type::Type* type) {
-    return EmitValue(out, type, 0);
-}
-
-bool ASTPrinter::EmitLoop(const ast::LoopStatement* stmt) {
-    auto emit_continuing = [this, stmt] {
-        if (stmt->continuing && !stmt->continuing->Empty()) {
-            if (!EmitBlock(stmt->continuing)) {
-                return false;
-            }
-        }
-        return true;
-    };
-
-    TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-    Line() << "while (true) {";
-    {
-        ScopedIndent si(this);
-        if (!EmitStatements(stmt->body->statements)) {
-            return false;
-        }
-        if (!emit_continuing_()) {
-            return false;
-        }
-    }
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitForLoop(const ast::ForLoopStatement* stmt) {
-    // Nest a for loop with a new block. In HLSL the initializer scope is not
-    // nested by the for-loop, so we may get variable redefinitions.
-    Line() << "{";
-    IncrementIndent();
-    TINT_DEFER({
-        DecrementIndent();
-        Line() << "}";
-    });
-
-    TextBuffer init_buf;
-    if (auto* init = stmt->initializer) {
-        TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
-        if (!EmitStatement(init)) {
-            return false;
-        }
-    }
-
-    TextBuffer cond_pre;
-    StringStream cond_buf;
-    if (auto* cond = stmt->condition) {
-        TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
-        if (!EmitExpression(cond_buf, cond)) {
-            return false;
-        }
-    }
-
-    TextBuffer cont_buf;
-    if (auto* cont = stmt->continuing) {
-        TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
-        if (!EmitStatement(cont)) {
-            return false;
-        }
-    }
-
-    // If the for-loop has a multi-statement conditional and / or continuing, then
-    // we cannot emit this as a regular for-loop in HLSL. Instead we need to
-    // generate a `while(true)` loop.
-    bool emit_as_loop = cond_pre.lines.size() > 0 || cont_buf.lines.size() > 1;
-
-    // If the for-loop has multi-statement initializer, or is going to be emitted
-    // as a `while(true)` loop, then declare the initializer statement(s) before
-    // the loop.
-    if (init_buf.lines.size() > 1 || (stmt->initializer && emit_as_loop)) {
-        current_buffer_->Append(init_buf);
-        init_buf.lines.clear();  // Don't emit the initializer again in the 'for'
-    }
-
-    if (emit_as_loop) {
-        auto emit_continuing = [&] {
-            current_buffer_->Append(cont_buf);
-            return true;
-        };
-
-        TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-        Line() << "while (true) {";
-        IncrementIndent();
-        TINT_DEFER({
-            DecrementIndent();
-            Line() << "}";
-        });
-
-        if (stmt->condition) {
-            current_buffer_->Append(cond_pre);
-            Line() << "if (!(" << cond_buf.str() << ")) { break; }";
-        }
-
-        if (!EmitStatements(stmt->body->statements)) {
-            return false;
-        }
-
-        if (!emit_continuing_()) {
-            return false;
-        }
-    } else {
-        // For-loop can be generated.
-        {
-            auto out = Line();
-            out << "for";
-            {
-                ScopedParen sp(out);
-
-                if (!init_buf.lines.empty()) {
-                    out << init_buf.lines[0].content << " ";
-                } else {
-                    out << "; ";
-                }
-
-                out << cond_buf.str() << "; ";
-
-                if (!cont_buf.lines.empty()) {
-                    out << tint::TrimSuffix(cont_buf.lines[0].content, ";");
-                }
-            }
-            out << " {";
-        }
-        {
-            auto emit_continuing = [] { return true; };
-            TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-            if (!EmitStatementsWithIndent(stmt->body->statements)) {
-                return false;
-            }
-        }
-        Line() << "}";
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitWhile(const ast::WhileStatement* stmt) {
-    TextBuffer cond_pre;
-    StringStream cond_buf;
-    {
-        auto* cond = stmt->condition;
-        TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
-        if (!EmitExpression(cond_buf, cond)) {
-            return false;
-        }
-    }
-
-    auto emit_continuing = [&] { return true; };
-    TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-
-    // If the while has a multi-statement conditional, then we cannot emit this
-    // as a regular while in HLSL. Instead we need to generate a `while(true)` loop.
-    bool emit_as_loop = cond_pre.lines.size() > 0;
-    if (emit_as_loop) {
-        Line() << "while (true) {";
-        IncrementIndent();
-        TINT_DEFER({
-            DecrementIndent();
-            Line() << "}";
-        });
-
-        current_buffer_->Append(cond_pre);
-        Line() << "if (!(" << cond_buf.str() << ")) { break; }";
-        if (!EmitStatements(stmt->body->statements)) {
-            return false;
-        }
-    } else {
-        // While can be generated.
-        {
-            auto out = Line();
-            out << "while";
-            {
-                ScopedParen sp(out);
-                out << cond_buf.str();
-            }
-            out << " {";
-        }
-        if (!EmitStatementsWithIndent(stmt->body->statements)) {
-            return false;
-        }
-        Line() << "}";
-    }
-
-    return true;
-}
-
-bool ASTPrinter::EmitMemberAccessor(StringStream& out, const ast::MemberAccessorExpression* expr) {
-    if (!EmitExpression(out, expr->object)) {
-        return false;
-    }
-    out << ".";
-
-    auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
-
-    return Switch(
-        sem,
-        [&](const sem::Swizzle*) {
-            // Swizzles output the name directly
-            out << expr->member->symbol.Name();
-            return true;
-        },
-        [&](const sem::StructMemberAccess* member_access) {
-            out << member_access->Member()->Name().Name();
-            return true;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitReturn(const ast::ReturnStatement* stmt) {
-    if (stmt->value) {
-        auto out = Line();
-        out << "return ";
-        if (!EmitExpression(out, stmt->value)) {
-            return false;
-        }
-        out << ";";
-    } else {
-        Line() << "return;";
-    }
-    return true;
-}
-
-bool ASTPrinter::EmitStatement(const ast::Statement* stmt) {
-    return Switch(
-        stmt,
-        [&](const ast::AssignmentStatement* a) {  //
-            return EmitAssign(a);
-        },
-        [&](const ast::BlockStatement* b) {  //
-            return EmitBlock(b);
-        },
-        [&](const ast::BreakStatement* b) {  //
-            return EmitBreak(b);
-        },
-        [&](const ast::BreakIfStatement* b) {  //
-            return EmitBreakIf(b);
-        },
-        [&](const ast::CallStatement* c) {  //
-            auto out = Line();
-            if (!EmitCall(out, c->expr)) {
-                return false;
-            }
-            out << ";";
-            return true;
-        },
-        [&](const ast::ContinueStatement* c) {  //
-            return EmitContinue(c);
-        },
-        [&](const ast::DiscardStatement* d) {  //
-            return EmitDiscard(d);
-        },
-        [&](const ast::IfStatement* i) {  //
-            return EmitIf(i);
-        },
-        [&](const ast::LoopStatement* l) {  //
-            return EmitLoop(l);
-        },
-        [&](const ast::ForLoopStatement* l) {  //
-            return EmitForLoop(l);
-        },
-        [&](const ast::WhileStatement* l) {  //
-            return EmitWhile(l);
-        },
-        [&](const ast::ReturnStatement* r) {  //
-            return EmitReturn(r);
-        },
-        [&](const ast::SwitchStatement* s) {  //
-            return EmitSwitch(s);
-        },
-        [&](const ast::VariableDeclStatement* v) {  //
-            return Switch(
-                v->variable,  //
-                [&](const ast::Var* var) { return EmitVar(var); },
-                [&](const ast::Let* let) { return EmitLet(let); },
-                [&](const ast::Const*) {
-                    return true;  // Constants are embedded at their use
-                },                //
-                TINT_ICE_ON_NO_MATCH);
-        },
-        [&](const ast::ConstAssert*) {
-            return true;  // Not emitted
-        },                //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
-    TINT_ASSERT(stmt->body.Length() == 1 && stmt->body[0]->ContainsDefault());
-
-    // FXC fails to compile a switch with just a default case, ignoring the
-    // default case body. We work around this here by emitting the default case
-    // without the switch.
-
-    // Emit the switch condition as-is if it has side-effects (e.g.
-    // function call). Note that we can ignore the result of the expression (if any).
-    if (auto* sem_cond = builder_.Sem().GetVal(stmt->condition); sem_cond->HasSideEffects()) {
-        auto out = Line();
-        if (!EmitExpression(out, stmt->condition)) {
-            return false;
-        }
-        out << ";";
-    }
-
-    // Emit "do { <default case body> } while(false);". We use a 'do' loop so
-    // that break statements work as expected, and make it 'while (false)' in
-    // case there isn't a break statement.
-    Line() << "do {";
-    {
-        ScopedIndent si(this);
-        if (!EmitStatements(stmt->body[0]->body->statements)) {
-            return false;
-        }
-    }
-    Line() << "} while (false);";
-    return true;
-}
-
-bool ASTPrinter::EmitSwitch(const ast::SwitchStatement* stmt) {
-    // BUG(crbug.com/tint/1188): work around default-only switches
-    if (stmt->body.Length() == 1 && stmt->body[0]->selectors.Length() == 1 &&
-        stmt->body[0]->ContainsDefault()) {
-        return EmitDefaultOnlySwitch(stmt);
-    }
-
-    {  // switch(expr) {
-        auto out = Line();
-        out << "switch(";
-        if (!EmitExpression(out, stmt->condition)) {
-            return false;
-        }
-        out << ") {";
-    }
-
-    {
-        ScopedIndent si(this);
-        for (size_t i = 0; i < stmt->body.Length(); i++) {
-            if (!EmitCase(stmt, i)) {
-                return false;
-            }
-        }
-    }
-
-    Line() << "}";
-
-    return true;
-}
-
-bool ASTPrinter::EmitType(StringStream& out,
-                          const core::type::Type* type,
-                          core::AddressSpace address_space,
-                          core::Access access,
-                          const std::string& name,
-                          bool* name_printed /* = nullptr */) {
-    if (name_printed) {
-        *name_printed = false;
-    }
-    switch (address_space) {
-        case core::AddressSpace::kStorage:
-            if (access != core::Access::kRead) {
-                out << "RW";
-            }
-            out << "ByteAddressBuffer";
-            return true;
-        case core::AddressSpace::kUniform: {
-            auto array_length = (type->Size() + 15) / 16;
-            out << "uint4 " << name << "[" << array_length << "]";
-            if (name_printed) {
-                *name_printed = true;
-            }
-            return true;
-        }
-        default:
-            break;
-    }
-
-    return Switch(
-        type,
-        [&](const core::type::Array* ary) {
-            const core::type::Type* base_type = ary;
-            std::vector<uint32_t> sizes;
-            while (auto* arr = base_type->As<core::type::Array>()) {
-                if (DAWN_UNLIKELY(arr->Count()->Is<core::type::RuntimeArrayCount>())) {
-                    TINT_ICE()
-                        << "runtime arrays may only exist in storage buffers, which should have "
-                           "been transformed into a ByteAddressBuffer";
-                }
-                const auto count = arr->ConstantCount();
-                if (!count) {
-                    diagnostics_.AddError(Source{}) << core::type::Array::kErrExpectedConstantCount;
-                    return false;
-                }
-
-                sizes.push_back(count.value());
-                base_type = arr->ElemType();
-            }
-            if (!EmitType(out, base_type, address_space, access, "")) {
-                return false;
-            }
-            if (!name.empty()) {
-                out << " " << name;
-                if (name_printed) {
-                    *name_printed = true;
-                }
-            }
-            for (uint32_t size : sizes) {
-                out << "[" << size << "]";
-            }
-            return true;
-        },
-        [&](const core::type::Bool*) {
-            out << "bool";
-            return true;
-        },
-        [&](const core::type::F32*) {
-            out << "float";
-            return true;
-        },
-        [&](const core::type::F16*) {
-            out << "float16_t";
-            return true;
-        },
-        [&](const core::type::I32*) {
-            out << "int";
-            return true;
-        },
-        [&](const core::type::Matrix* mat) {
-            if (mat->Type()->Is<core::type::F16>()) {
-                // Use matrix<type, N, M> for f16 matrix
-                out << "matrix<";
-                if (!EmitType(out, mat->Type(), address_space, access, "")) {
-                    return false;
-                }
-                out << ", " << mat->Columns() << ", " << mat->Rows() << ">";
-                return true;
-            }
-            if (!EmitType(out, mat->Type(), address_space, access, "")) {
-                return false;
-            }
-            // Note: HLSL's matrices are declared as <type>NxM, where N is the
-            // number of rows and M is the number of columns. Despite HLSL's
-            // matrices being column-major by default, the index operator and
-            // initializers actually operate on row-vectors, where as WGSL operates
-            // on column vectors. To simplify everything we use the transpose of the
-            // matrices. See:
-            // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#matrix-ordering
-            out << mat->Columns() << "x" << mat->Rows();
-            return true;
-        },
-        [&](const core::type::Pointer*) -> bool {
-            TINT_ICE() << "Attempting to emit pointer type. These should have been removed with "
-                          "the SimplifyPointers transform";
-        },
-        [&](const core::type::Sampler* sampler) {
-            out << "Sampler";
-            if (sampler->IsComparison()) {
-                out << "Comparison";
-            }
-            out << "State";
-            return true;
-        },
-        [&](const core::type::Struct* str) {
-            out << StructName(str);
-            return true;
-        },
-        [&](const core::type::Texture* tex) {
-            if (DAWN_UNLIKELY(tex->Is<core::type::ExternalTexture>())) {
-                TINT_ICE() << "Multiplanar external texture transform was not run.";
-            }
-
-            auto* storage = tex->As<core::type::StorageTexture>();
-            auto* ms = tex->As<core::type::MultisampledTexture>();
-            auto* depth_ms = tex->As<core::type::DepthMultisampledTexture>();
-            auto* sampled = tex->As<core::type::SampledTexture>();
-
-            if (storage && storage->Access() != core::Access::kRead) {
-                out << "RW";
-            }
-            out << "Texture";
-
-            switch (tex->Dim()) {
-                case core::type::TextureDimension::k1d:
-                    out << "1D";
-                    break;
-                case core::type::TextureDimension::k2d:
-                    out << ((ms || depth_ms) ? "2DMS" : "2D");
-                    break;
-                case core::type::TextureDimension::k2dArray:
-                    out << ((ms || depth_ms) ? "2DMSArray" : "2DArray");
-                    break;
-                case core::type::TextureDimension::k3d:
-                    out << "3D";
-                    break;
-                case core::type::TextureDimension::kCube:
-                    out << "Cube";
-                    break;
-                case core::type::TextureDimension::kCubeArray:
-                    out << "CubeArray";
-                    break;
-                default:
-                    TINT_UNREACHABLE() << "unexpected TextureDimension " << tex->Dim();
-            }
-
-            if (storage) {
-                auto* component = ImageFormatToRWtextureType(storage->TexelFormat());
-                if (DAWN_UNLIKELY(!component)) {
-                    TINT_ICE() << "Unsupported StorageTexture TexelFormat: "
-                               << static_cast<int>(storage->TexelFormat());
-                }
-                out << "<" << component << ">";
-            } else if (depth_ms) {
-                out << "<float4>";
-            } else if (sampled || ms) {
-                auto* subtype = sampled ? sampled->Type() : ms->Type();
-                out << "<";
-                if (subtype->Is<core::type::F32>()) {
-                    out << "float4";
-                } else if (subtype->Is<core::type::I32>()) {
-                    out << "int4";
-                } else if (DAWN_LIKELY(subtype->Is<core::type::U32>())) {
-                    out << "uint4";
-                } else {
-                    TINT_ICE() << "Unsupported multisampled texture type";
-                }
-                out << ">";
-            }
-            return true;
-        },
-        [&](const core::type::U32*) {
-            out << "uint";
-            return true;
-        },
-        [&](const core::type::Vector* vec) {
-            auto width = vec->Width();
-            if (vec->Type()->Is<core::type::F32>() && width >= 1 && width <= 4) {
-                out << "float" << width;
-            } else if (vec->Type()->Is<core::type::I32>() && width >= 1 && width <= 4) {
-                out << "int" << width;
-            } else if (vec->Type()->Is<core::type::U32>() && width >= 1 && width <= 4) {
-                out << "uint" << width;
-            } else if (vec->Type()->Is<core::type::Bool>() && width >= 1 && width <= 4) {
-                out << "bool" << width;
-            } else {
-                // For example, use "vector<float16_t, N>" for f16 vector.
-                out << "vector<";
-                if (!EmitType(out, vec->Type(), address_space, access, "")) {
-                    return false;
-                }
-                out << ", " << width << ">";
-            }
-            return true;
-        },
-        [&](const core::type::Atomic* atomic) {
-            return EmitType(out, atomic->Type(), address_space, access, name);
-        },
-        [&](const core::type::Void*) {
-            out << "void";
-            return true;
-        },  //
-        TINT_ICE_ON_NO_MATCH);
-}
-
-bool ASTPrinter::EmitTypeAndName(StringStream& out,
-                                 const core::type::Type* type,
-                                 core::AddressSpace address_space,
-                                 core::Access access,
-                                 const std::string& name) {
-    bool name_printed = false;
-    if (!EmitType(out, type, address_space, access, name, &name_printed)) {
-        return false;
-    }
-    if (!name.empty() && !name_printed) {
-        out << " " << name;
-    }
-    return true;
-}
-
-bool ASTPrinter::EmitStructType(TextBuffer* b,
-                                const core::type::Struct* str,
-                                VectorRef<const ast::StructMember*> ast_struct_members) {
-    auto it = emitted_structs_.emplace(str);
-    if (!it.second) {
-        return true;
-    }
-
-    const auto struct_type_members = str->Members();
-    size_t struct_type_member_length = struct_type_members.Length();
-    TINT_ASSERT(ast_struct_members.IsEmpty() ||
-                (struct_type_member_length == ast_struct_members.Length()));
-
-    Line(b) << "struct " << StructName(str) << " {";
-    {
-        ScopedIndent si(b);
-        for (size_t i = 0; i < struct_type_member_length; ++i) {
-            auto* mem = struct_type_members[i];
-            auto mem_name = mem->Name().Name();
-            auto* ty = mem->Type();
-            auto out = Line(b);
-            std::string pre, post;
-
-            auto& attributes = mem->Attributes();
-
-            if (auto location = attributes.location) {
-                auto& pipeline_stage_uses = str->PipelineStageUses();
-                if (DAWN_UNLIKELY(pipeline_stage_uses.Count() != 1)) {
-                    TINT_ICE() << "invalid entry point IO struct uses";
-                }
-                if (pipeline_stage_uses.Contains(core::type::PipelineStageUsage::kVertexInput)) {
-                    post += " : TEXCOORD" + std::to_string(location.value());
-                } else if (pipeline_stage_uses.Contains(
-                               core::type::PipelineStageUsage::kVertexOutput)) {
-                    post += " : TEXCOORD" + std::to_string(location.value());
-                } else if (pipeline_stage_uses.Contains(
-                               core::type::PipelineStageUsage::kFragmentInput)) {
-                    post += " : TEXCOORD" + std::to_string(location.value());
-                } else if (DAWN_LIKELY(pipeline_stage_uses.Contains(
-                               core::type::PipelineStageUsage::kFragmentOutput))) {
-                    if (auto blend_src = attributes.blend_src) {
-                        post +=
-                            " : SV_Target" + std::to_string(location.value() + blend_src.value());
-                    } else {
-                        post += " : SV_Target" + std::to_string(location.value());
-                    }
-
-                } else {
-                    TINT_ICE() << "invalid use of location attribute";
-                }
-            }
-            if (auto builtin = attributes.builtin) {
-                auto name = builtin_to_attribute(builtin.value());
-                if (name.empty()) {
-                    diagnostics_.AddError(Source{}) << "unsupported builtin";
-                    return false;
-                }
-                post += " : " + name;
-            }
-            if (auto interpolation = attributes.interpolation) {
-                auto mod = interpolation_to_modifiers(interpolation->type, interpolation->sampling);
-                if (mod.empty()) {
-                    diagnostics_.AddError(Source{}) << "unsupported interpolation";
-                    return false;
-                }
-                pre += mod;
-            }
-            if (attributes.invariant) {
-                // Note: `precise` is not exactly the same as `invariant`, but is
-                // stricter and therefore provides the necessary guarantees.
-                // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
-                pre += "precise ";
-            }
-            if (!ast_struct_members.IsEmpty() &&
-                ast::HasAttribute<ast::transform::CanonicalizeEntryPointIO::HLSLClipDistance1>(
-                    ast_struct_members[i]->attributes)) {
-                post += " : SV_ClipDistance1";
-            }
-
-            out << pre;
-            if (!EmitTypeAndName(out, ty, core::AddressSpace::kUndefined, core::Access::kReadWrite,
-                                 mem_name)) {
-                return false;
-            }
-            out << post << ";";
-        }
-    }
-
-    Line(b) << "};";
-    return true;
-}
-
-bool ASTPrinter::EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr) {
-    switch (expr->op) {
-        case core::UnaryOp::kIndirection:
-        case core::UnaryOp::kAddressOf:
-            return EmitExpression(out, expr->expr);
-        case core::UnaryOp::kComplement:
-            out << "~";
-            break;
-        case core::UnaryOp::kNot:
-            out << "!";
-            break;
-        case core::UnaryOp::kNegation:
-            out << "-";
-            break;
-    }
-    out << "(";
-
-    if (!EmitExpression(out, expr->expr)) {
-        return false;
-    }
-
-    out << ")";
-
-    return true;
-}
-
-bool ASTPrinter::EmitVar(const ast::Var* var) {
-    auto* sem = builder_.Sem().Get(var);
-    auto* type = sem->Type()->UnwrapRef();
-
-    auto out = Line();
-    if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), var->name->symbol.Name())) {
-        return false;
-    }
-
-    out << " = ";
-
-    if (var->initializer) {
-        if (!EmitExpression(out, var->initializer)) {
-            return false;
-        }
-    } else {
-        if (!EmitZeroValue(out, type)) {
-            return false;
-        }
-    }
-    out << ";";
-
-    return true;
-}
-
-bool ASTPrinter::EmitLet(const ast::Let* let) {
-    auto* sem = builder_.Sem().Get(let);
-    auto* type = sem->Type()->UnwrapRef();
-
-    auto out = Line();
-    if (!EmitTypeAndName(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
-                         let->name->symbol.Name())) {
-        return false;
-    }
-    out << " = ";
-    if (!EmitExpression(out, let->initializer)) {
-        return false;
-    }
-    out << ";";
-
-    return true;
-}
-
-template <typename F>
-bool ASTPrinter::CallBuiltinHelper(StringStream& out,
-                                   const ast::CallExpression* call,
-                                   const sem::BuiltinFn* builtin,
-                                   F&& build) {
-    // Generate the helper function if it hasn't been created already
-    auto fn = tint::GetOrAdd(builtins_, builtin, [&]() -> std::string {
-        TextBuffer b;
-        TINT_DEFER(helpers_.Append(b));
-
-        auto fn_name = UniqueIdentifier(std::string("tint_") + wgsl::str(builtin->Fn()));
-        std::vector<std::string> parameter_names;
-        {
-            auto decl = Line(&b);
-            if (!EmitTypeAndName(decl, builtin->ReturnType(), core::AddressSpace::kUndefined,
-                                 core::Access::kUndefined, fn_name)) {
-                return "";
-            }
-            {
-                ScopedParen sp(decl);
-                for (auto* param : builtin->Parameters()) {
-                    if (!parameter_names.empty()) {
-                        decl << ", ";
-                    }
-                    auto param_name = "param_" + std::to_string(parameter_names.size());
-                    const auto* ty = param->Type();
-                    if (auto* ptr = ty->As<core::type::Pointer>()) {
-                        decl << "inout ";
-                        ty = ptr->StoreType();
-                    }
-                    if (!EmitTypeAndName(decl, ty, core::AddressSpace::kUndefined,
-                                         core::Access::kUndefined, param_name)) {
-                        return "";
-                    }
-                    parameter_names.emplace_back(std::move(param_name));
-                }
-            }
-            decl << " {";
-        }
-        {
-            ScopedIndent si(&b);
-            if (!build(&b, parameter_names)) {
-                return "";
-            }
-        }
-        Line(&b) << "}";
-        Line(&b);
-        return fn_name;
-    });
-
-    if (fn.empty()) {
-        return false;
-    }
-
-    // Call the helper
-    out << fn;
-    {
-        ScopedParen sp(out);
-        bool first = true;
-        for (auto* arg : call->args) {
-            if (!first) {
-                out << ", ";
-            }
-            first = false;
-            if (!EmitExpression(out, arg)) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-std::string ASTPrinter::StructName(const core::type::Struct* s) {
-    auto name = s->Name().Name();
-    if (HasPrefix(name, "__")) {
-        name = tint::GetOrAdd(builtin_struct_names_, s,
-                              [&] { return UniqueIdentifier(name.substr(2)); });
-    }
-    return name;
-}
-
-std::string ASTPrinter::UniqueIdentifier(const std::string& prefix /* = "" */) {
-    return builder_.Symbols().New(prefix).Name();
-}
-
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h b/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h
deleted file mode 100644
index 77aa614..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer.h
+++ /dev/null
@@ -1,638 +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_HLSL_WRITER_AST_PRINTER_AST_PRINTER_H_
-#define SRC_TINT_LANG_HLSL_WRITER_AST_PRINTER_AST_PRINTER_H_
-
-#include <string>
-#include <tuple>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/core/builtin_value.h"
-#include "src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.h"
-#include "src/tint/lang/hlsl/writer/common/options.h"
-#include "src/tint/lang/wgsl/program/program_builder.h"
-#include "src/tint/utils/containers/scope_stack.h"
-#include "src/tint/utils/math/hash.h"
-#include "src/tint/utils/text_generator/text_generator.h"
-
-// Forward declarations
-namespace tint::sem {
-class BuiltinFn;
-class Call;
-class ValueConstructor;
-class ValueConversion;
-}  // namespace tint::sem
-
-namespace tint::hlsl::writer {
-
-/// The result of sanitizing a program for generation.
-struct SanitizedResult {
-    /// Constructor
-    SanitizedResult();
-    /// Destructor
-    ~SanitizedResult();
-    /// Move constructor
-    SanitizedResult(SanitizedResult&&);
-
-    /// The sanitized program.
-    Program program;
-    /// Indices into the array_length_from_uniform binding that are statically
-    /// used.
-    std::unordered_set<uint32_t> used_array_length_from_uniform_indices;
-};
-
-/// Sanitize a program in preparation for generating HLSL.
-/// @param program the input program
-/// @param options The HLSL generator options.
-/// @returns the sanitized program and any supplementary information
-SanitizedResult Sanitize(const Program& program, const Options& options);
-
-/// Implementation class for HLSL generator
-class ASTPrinter : public tint::TextGenerator {
-  public:
-    /// Constructor
-    /// @param program the program to generate
-    explicit ASTPrinter(const Program& program);
-    ~ASTPrinter() override;
-
-    /// @returns true on successful generation; false otherwise
-    bool Generate();
-
-    /// Handles an index accessor expression
-    /// @param out the output stream
-    /// @param expr the expression to emit
-    /// @returns true if the index accessor was emitted
-    bool EmitIndexAccessor(StringStream& out, const ast::IndexAccessorExpression* expr);
-    /// Handles an assignment statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitAssign(const ast::AssignmentStatement* stmt);
-    /// Handles generating a binary expression
-    /// @param out the output stream
-    /// @param expr the binary expression
-    /// @returns true if the expression was emitted, false otherwise
-    bool EmitBinary(StringStream& out, const ast::BinaryExpression* expr);
-    /// Handles generating a bitcast expression
-    /// @param out the output stream
-    /// @param expr the as expression
-    /// @returns true if the bitcast was emitted
-    bool EmitBitcastCall(StringStream& out, const ast::CallExpression* expr);
-    /// Emits a list of statements
-    /// @param stmts the statement list
-    /// @returns true if the statements were emitted successfully
-    bool EmitStatements(VectorRef<const ast::Statement*> stmts);
-    /// Emits a list of statements with an indentation
-    /// @param stmts the statement list
-    /// @returns true if the statements were emitted successfully
-    bool EmitStatementsWithIndent(VectorRef<const ast::Statement*> stmts);
-    /// Handles a block statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBlock(const ast::BlockStatement* stmt);
-    /// Handles a break statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBreak(const ast::BreakStatement* stmt);
-    /// Handles a break-if statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitBreakIf(const ast::BreakIfStatement* stmt);
-    /// Handles generating a call expression
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @returns true if the call expression is emitted
-    bool EmitCall(StringStream& out, const ast::CallExpression* expr);
-    /// Handles generating a function call expression
-    /// @param out the output stream
-    /// @param call the call expression
-    /// @param function the function being called
-    /// @returns true if the expression is emitted
-    bool EmitFunctionCall(StringStream& out, const sem::Call* call, const sem::Function* function);
-    /// Handles generating a builtin call expression
-    /// @param out the output stream
-    /// @param call the call expression
-    /// @param builtin the builtin being called
-    /// @returns true if the expression is emitted
-    bool EmitBuiltinCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn* builtin);
-    /// Handles generating a value conversion expression
-    /// @param out the output stream
-    /// @param call the call expression
-    /// @param conv the value conversion
-    /// @returns true if the expression is emitted
-    bool EmitValueConversion(StringStream& out,
-                             const sem::Call* call,
-                             const sem::ValueConversion* conv);
-    /// Handles generating a value constructor expression
-    /// @param out the output stream
-    /// @param call the call expression
-    /// @param ctor the value constructor
-    /// @returns true if the expression is emitted
-    bool EmitValueConstructor(StringStream& out,
-                              const sem::Call* call,
-                              const sem::ValueConstructor* ctor);
-    /// Handles generating a call expression to a
-    /// DecomposeMemoryAccess::Intrinsic for a uniform buffer
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param intrinsic the DecomposeMemoryAccess::Intrinsic
-    /// @returns true if the call expression is emitted
-    bool EmitUniformBufferAccess(StringStream& out,
-                                 const ast::CallExpression* expr,
-                                 const DecomposeMemoryAccess::Intrinsic* intrinsic);
-    /// Handles generating a call expression to a
-    /// DecomposeMemoryAccess::Intrinsic for a storage buffer
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param intrinsic the DecomposeMemoryAccess::Intrinsic
-    /// @returns true if the call expression is emitted
-    bool EmitStorageBufferAccess(StringStream& out,
-                                 const ast::CallExpression* expr,
-                                 const DecomposeMemoryAccess::Intrinsic* intrinsic);
-    /// Handles generating a barrier intrinsic call
-    /// @param out the output stream
-    /// @param builtin the semantic information for the barrier builtin
-    /// @returns true if the call expression is emitted
-    bool EmitBarrierCall(StringStream& out, const sem::BuiltinFn* builtin);
-    /// Handles generating an atomic intrinsic call for a storage buffer variable
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param intrinsic the atomic intrinsic
-    /// @returns true if the call expression is emitted
-    bool EmitStorageAtomicCall(StringStream& out,
-                               const ast::CallExpression* expr,
-                               const DecomposeMemoryAccess::Intrinsic* intrinsic);
-    /// Handles generating the helper function for the atomic intrinsic function
-    /// @param func the function
-    /// @param intrinsic the atomic intrinsic
-    /// @returns true if the function is emitted
-    bool EmitStorageAtomicIntrinsic(const ast::Function* func,
-                                    const DecomposeMemoryAccess::Intrinsic* intrinsic);
-    /// Handles generating an atomic intrinsic call for a workgroup variable
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the atomic builtin
-    /// @returns true if the call expression is emitted
-    bool EmitWorkgroupAtomicCall(StringStream& out,
-                                 const ast::CallExpression* expr,
-                                 const sem::BuiltinFn* builtin);
-    /// Handles generating a call to a texture function (`textureSample`,
-    /// `textureSampleGrad`, etc)
-    /// @param out the output stream
-    /// @param call the call expression
-    /// @param builtin the semantic information for the texture builtin
-    /// @returns true if the call expression is emitted
-    bool EmitTextureCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `select()` builtin
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @returns true if the call expression is emitted
-    bool EmitSelectCall(StringStream& out, const ast::CallExpression* expr);
-    /// Handles generating a call to the `modf()` builtin
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitModfCall(StringStream& out,
-                      const ast::CallExpression* expr,
-                      const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `frexp()` builtin
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitFrexpCall(StringStream& out,
-                       const ast::CallExpression* expr,
-                       const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `degrees()` builtin
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitDegreesCall(StringStream& out,
-                         const ast::CallExpression* expr,
-                         const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `radians()` builtin
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitRadiansCall(StringStream& out,
-                         const ast::CallExpression* expr,
-                         const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `sign()` builtin
-    /// @param out the output stream
-    /// @param call the call semantic node
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitSignCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn* builtin);
-    /// Handles generating a call to data packing builtin
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitDataPackingCall(StringStream& out,
-                             const ast::CallExpression* expr,
-                             const sem::BuiltinFn* builtin);
-    /// Handles generating a call to data unpacking builtin
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitDataUnpackingCall(StringStream& out,
-                               const ast::CallExpression* expr,
-                               const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `quantizeToF16()` intrinsic
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitQuantizeToF16Call(StringStream& out,
-                               const ast::CallExpression* expr,
-                               const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `trunc()` intrinsic
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitTruncCall(StringStream& out,
-                       const ast::CallExpression* expr,
-                       const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the builtins defined in the language extension
-    /// `packed_4x8_integer_dot_product`.
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitPacked4x8IntegerDotProductBuiltinCall(StringStream& out,
-                                                   const ast::CallExpression* expr,
-                                                   const sem::BuiltinFn* builtin);
-    /// Handles generating a call to the `WaveReadLaneAt` intrinsic for subgroupShuffleXor,
-    /// subgroupShuffleUp and subgroupShuffleDown
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitSubgroupShuffleBuiltinCall(StringStream& out,
-                                        const ast::CallExpression* expr,
-                                        const sem::BuiltinFn* builtin);
-
-    /// Handles generating a call to the `WavePrefixSum` and `WavePrefixProduct` intrinsic for
-    /// subgroupInclusiveSum and subgroupInclusiveMul
-    /// @param out the output stream
-    /// @param expr the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @returns true if the call expression is emitted
-    bool EmitSubgroupInclusiveBuiltinCall(StringStream& out,
-                                          const ast::CallExpression* expr,
-                                          const sem::BuiltinFn* builtin);
-
-    /// Handles a case statement
-    /// @param s the switch statement
-    /// @param case_idx the index of the switch case in the switch statement
-    /// @returns true if the statement was emitted successfully
-    bool EmitCase(const ast::SwitchStatement* s, size_t case_idx);
-    /// Handles generating a discard statement
-    /// @param stmt the discard statement
-    /// @returns true if the statement was successfully emitted
-    bool EmitDiscard(const ast::DiscardStatement* stmt);
-    /// Handles a continue statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted successfully
-    bool EmitContinue(const ast::ContinueStatement* stmt);
-    /// Handles generating an Expression
-    /// @param out the output stream
-    /// @param expr the expression
-    /// @returns true if the expression was emitted
-    bool EmitExpression(StringStream& out, const ast::Expression* expr);
-    /// Handles generating an Expression for texture or storage buffer call arguments. This is
-    /// specifically to work around a DXC bug around passing signed integer splatted constants as
-    /// args to these functions (see crbug.com/tint/1976)
-    /// @param out the output stream
-    /// @param expr the expression
-    /// @returns true if the expression was emitted
-    bool EmitTextureOrStorageBufferCallArgExpression(StringStream& out,
-                                                     const ast::Expression* expr);
-    /// Handles generating a function
-    /// @param func the function to generate
-    /// @returns true if the function was emitted
-    bool EmitFunction(const ast::Function* func);
-    /// Handles emitting the function body if it discards to work around a FXC
-    /// compilation bug.
-    /// @param func the function with the body to emit
-    /// @returns true if the function was emitted
-    bool EmitFunctionBodyWithDiscard(const ast::Function* func);
-    /// Handles emitting a global variable
-    /// @param global the global variable
-    /// @returns true on success
-    bool EmitGlobalVariable(const ast::Variable* global);
-
-    /// Handles emitting a global variable with the uniform address space
-    /// @param var the AST node for the 'var'
-    /// @param sem the semantic node for the 'var'
-    /// @returns true on success
-    bool EmitUniformVariable(const ast::Var* var, const sem::Variable* sem);
-
-    /// Handles emitting a global variable with the storage address space
-    /// @param var the AST node for the 'var'
-    /// @param sem the semantic node for the 'var'
-    /// @returns true on success
-    bool EmitStorageVariable(const ast::Var* var, const sem::Variable* sem);
-
-    /// Handles emitting a global variable with the handle address space
-    /// @param var the AST node for the 'var'
-    /// @param sem the semantic node for the 'var'
-    /// @returns true on success
-    bool EmitHandleVariable(const ast::Var* var, const sem::Variable* sem);
-
-    /// Handles emitting a global variable with the private address space
-    /// @param var the global variable
-    /// @returns true on success
-    bool EmitPrivateVariable(const sem::Variable* var);
-
-    /// Handles emitting a global variable with the workgroup address space
-    /// @param var the global variable
-    /// @returns true on success
-    bool EmitWorkgroupVariable(const sem::Variable* var);
-
-    /// Handles emitting the entry point function
-    /// @param func the entry point
-    /// @returns true if the entry point function was emitted
-    bool EmitEntryPointFunction(const ast::Function* func);
-    /// Handles an if statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitIf(const ast::IfStatement* stmt);
-    /// Handles a constant value
-    /// @param out the output stream
-    /// @param constant the constant value to emit
-    /// @param is_variable_initializer true if the constant is used as the RHS of a variable
-    /// initializer
-    /// @returns true if the constant value was successfully emitted
-    bool EmitConstant(StringStream& out,
-                      const core::constant::Value* constant,
-                      bool is_variable_initializer);
-    /// Handles a literal
-    /// @param out the output stream
-    /// @param lit the literal to emit
-    /// @returns true if the literal was successfully emitted
-    bool EmitLiteral(StringStream& out, const ast::LiteralExpression* lit);
-    /// Handles a loop statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitLoop(const ast::LoopStatement* stmt);
-    /// Handles a for loop statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitForLoop(const ast::ForLoopStatement* stmt);
-    /// Handles a while statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitWhile(const ast::WhileStatement* stmt);
-    /// Handles generating an identifier expression
-    /// @param out the output stream
-    /// @param expr the identifier expression
-    /// @returns true if the identifeir was emitted
-    bool EmitIdentifier(StringStream& out, const ast::IdentifierExpression* expr);
-    /// Handles a member accessor expression
-    /// @param out the output stream
-    /// @param expr the member accessor expression
-    /// @returns true if the member accessor was emitted
-    bool EmitMemberAccessor(StringStream& out, const ast::MemberAccessorExpression* expr);
-    /// Handles return statements
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was successfully emitted
-    bool EmitReturn(const ast::ReturnStatement* stmt);
-    /// Handles statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitStatement(const ast::Statement* stmt);
-    /// Handles generating a switch statement
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitSwitch(const ast::SwitchStatement* stmt);
-    // Handles generating a switch statement with only a default case
-    /// @param stmt the statement to emit
-    /// @returns true if the statement was emitted
-    bool EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt);
-    /// Handles generating type
-    /// @param out the output stream
-    /// @param type the type to generate
-    /// @param address_space the address space of the variable
-    /// @param access the access control type of the variable
-    /// @param name the name of the variable, used for array emission.
-    /// @param name_printed (optional) if not nullptr and an array was printed
-    /// then the boolean is set to true.
-    /// @returns true if the type is emitted
-    bool EmitType(StringStream& out,
-                  const core::type::Type* type,
-                  core::AddressSpace address_space,
-                  core::Access access,
-                  const std::string& name,
-                  bool* name_printed = nullptr);
-    /// Handles generating type and name
-    /// @param out the output stream
-    /// @param type the type to generate
-    /// @param address_space the address space of the variable
-    /// @param access the access control type of the variable
-    /// @param name the name to emit
-    /// @returns true if the type is emitted
-    bool EmitTypeAndName(StringStream& out,
-                         const core::type::Type* type,
-                         core::AddressSpace address_space,
-                         core::Access access,
-                         const std::string& name);
-    /// Handles generating a structure declaration. If the structure has already been emitted, then
-    /// this function will simply return `true` without emitting anything.
-    /// @param buffer the text buffer that the type declaration will be written to
-    /// @param ty the struct to generate
-    /// @param ast_struct_members the definition of struct members in the AST if not empty.
-    /// @returns true if the struct is emitted
-    bool EmitStructType(TextBuffer* buffer,
-                        const core::type::Struct* ty,
-                        VectorRef<const ast::StructMember*> ast_struct_members = Empty);
-    /// Handles a unary op expression
-    /// @param out the output stream
-    /// @param expr the expression to emit
-    /// @returns true if the expression was emitted
-    bool EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr);
-    /// Emits `value` for the given type
-    /// @param out the output stream
-    /// @param type the type to emit the value for
-    /// @param value the value to emit
-    /// @returns true if the value was successfully emitted.
-    bool EmitValue(StringStream& out, const core::type::Type* type, int value);
-    /// Emits the zero value for the given type
-    /// @param out the output stream
-    /// @param type the type to emit the value for
-    /// @returns true if the zero value was successfully emitted.
-    bool EmitZeroValue(StringStream& out, const core::type::Type* type);
-    /// Handles generating a 'var' declaration
-    /// @param var the variable to generate
-    /// @returns true if the variable was emitted
-    bool EmitVar(const ast::Var* var);
-    /// Handles generating a 'let' declaration
-    /// @param let the variable to generate
-    /// @returns true if the variable was emitted
-    bool EmitLet(const ast::Let* let);
-    /// Emits call to a helper vector assignment function for the input assignment
-    /// statement and vector type. This is used to work around FXC issues where
-    /// assignments to vectors with dynamic indices cause compilation failures.
-    /// @param stmt assignment statement that corresponds to a vector assignment
-    /// via an accessor expression
-    /// @param vec the vector type being assigned to
-    /// @returns true on success
-    bool EmitDynamicVectorAssignment(const ast::AssignmentStatement* stmt,
-                                     const core::type::Vector* vec);
-    /// Emits call to a helper matrix assignment function for the input assignment
-    /// statement and matrix type. This is used to work around FXC issues where
-    /// assignment of a vector to a matrix with a dynamic index causes compilation
-    /// failures.
-    /// @param stmt assignment statement that corresponds to a matrix assignment
-    /// via an accessor expression
-    /// @param mat the matrix type being assigned to
-    /// @returns true on success
-    bool EmitDynamicMatrixVectorAssignment(const ast::AssignmentStatement* stmt,
-                                           const core::type::Matrix* mat);
-    /// Emits call to a helper matrix assignment function for the input assignment
-    /// statement and matrix type. This is used to work around FXC issues where
-    /// assignment of a scalar to a matrix with at least one dynamic index causes
-    /// compilation failures.
-    /// @param stmt assignment statement that corresponds to a matrix assignment
-    /// via an accessor expression
-    /// @param mat the matrix type being assigned to
-    /// @returns true on success
-    bool EmitDynamicMatrixScalarAssignment(const ast::AssignmentStatement* stmt,
-                                           const core::type::Matrix* mat);
-
-    /// Handles generating a builtin method name
-    /// @param builtin the semantic info for the builtin
-    /// @returns the name or "" if not valid
-    std::string generate_builtin_name(const sem::BuiltinFn* builtin);
-    /// Converts a builtin to an attribute name
-    /// @param builtin the builtin to convert
-    /// @returns the string name of the builtin or blank on error
-    std::string builtin_to_attribute(core::BuiltinValue builtin) const;
-
-    /// Converts interpolation attributes to a HLSL modifiers
-    /// @param type the interpolation type
-    /// @param sampling the interpolation sampling
-    /// @returns the string name of the attribute or blank on error
-    std::string interpolation_to_modifiers(core::InterpolationType type,
-                                           core::InterpolationSampling sampling) const;
-
-  private:
-    enum class VarType { kIn, kOut };
-
-    struct EntryPointData {
-        std::string struct_name;
-        std::string var_name;
-    };
-
-    struct DMAIntrinsic {
-        DecomposeMemoryAccess::Intrinsic::Op op;
-        DecomposeMemoryAccess::Intrinsic::DataType type;
-        bool operator==(const DMAIntrinsic& rhs) const { return op == rhs.op && type == rhs.type; }
-        /// Hasher is a std::hash function for DMAIntrinsic
-        struct Hasher {
-            /// @param i the DMAIntrinsic to hash
-            /// @returns the hash of `i`
-            inline std::size_t operator()(const DMAIntrinsic& i) const {
-                return Hash(i.op, i.type);
-            }
-        };
-    };
-
-    /// The map key for two semantic types.
-    using BinaryType =
-        tint::UnorderedKeyWrapper<std::tuple<const core::type::Type*, const core::type::Type*>>;
-
-    /// CallBuiltinHelper will call the builtin helper function, creating it
-    /// if it hasn't been built already. If the builtin needs to be built then
-    /// CallBuiltinHelper will generate the function signature and will call
-    /// `build` to emit the body of the function.
-    /// @param out the output stream
-    /// @param call the call expression
-    /// @param builtin the semantic information for the builtin
-    /// @param build a function with the signature:
-    ///        `bool(TextBuffer* buffer, const std::vector<std::string>& params)`
-    ///        Where:
-    ///          `buffer` is the body of the generated function
-    ///          `params` is the name of all the generated function parameters
-    /// @returns true if the call expression is emitted
-    template <typename F>
-    bool CallBuiltinHelper(StringStream& out,
-                           const ast::CallExpression* call,
-                           const sem::BuiltinFn* builtin,
-                           F&& build);
-
-    /// @param s the structure
-    /// @returns the name of the structure, taking special care of builtin structures that start
-    /// with double underscores. If the structure is a builtin, then the returned name will be a
-    /// unique name without the leading underscores.
-    std::string StructName(const core::type::Struct* s);
-
-    /// @return a new, unique identifier with the given prefix.
-    /// @param prefix optional prefix to apply to the generated identifier. If empty "tint_symbol"
-    /// will be used.
-    std::string UniqueIdentifier(const std::string& prefix = "");
-
-    /// Alias for builder_.TypeOf(ptr)
-    template <typename T>
-    auto TypeOf(T* ptr) {
-        return builder_.TypeOf(ptr);
-    }
-
-    ProgramBuilder builder_;
-
-    /// Helper functions emitted at the top of the output
-    TextBuffer helpers_;
-
-    /// Map of builtin structure to unique generated name
-    std::unordered_map<const core::type::Struct*, std::string> builtin_struct_names_;
-    std::function<bool()> emit_continuing_;
-    std::unordered_map<const core::type::Matrix*, std::string> matrix_scalar_inits_;
-    std::unordered_map<const sem::BuiltinFn*, std::string> builtins_;
-    // Polyfill functions for bitcast expression, BinaryType indicates the source type and the
-    // destination type.
-    std::unordered_map<BinaryType, std::string> bitcast_funcs_;
-    std::unordered_map<const core::type::Vector*, std::string> dynamic_vector_write_;
-    std::unordered_map<const core::type::Matrix*, std::string> dynamic_matrix_vector_write_;
-    std::unordered_map<const core::type::Matrix*, std::string> dynamic_matrix_scalar_write_;
-    std::unordered_map<const core::type::Type*, std::string> value_or_one_if_zero_;
-    std::unordered_set<const core::type::Struct*> emitted_structs_;
-
-    // The line index in current_buffer_ of the current global declaration / function.
-    size_t global_insertion_point_ = 0;
-};
-
-}  // namespace tint::hlsl::writer
-
-#endif  // SRC_TINT_LANG_HLSL_WRITER_AST_PRINTER_AST_PRINTER_H_
diff --git a/src/tint/lang/hlsl/writer/ast_printer/ast_printer_test.cc b/src/tint/lang/hlsl/writer/ast_printer/ast_printer_test.cc
deleted file mode 100644
index faa95bf..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/ast_printer_test.cc
+++ /dev/null
@@ -1,111 +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/hlsl/writer/ast_printer/helper_test.h"
-
-#include "src/tint/lang/hlsl/writer/writer.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest = TestHelper;
-
-TEST_F(HlslASTPrinterTest, InvalidProgram) {
-    Diagnostics().AddError(Source{}) << "make the program invalid";
-    ASSERT_FALSE(IsValid());
-    auto program = resolver::Resolve(*this);
-    ASSERT_FALSE(program.IsValid());
-    auto result = Generate(program, Options{});
-    EXPECT_NE(result, Success);
-    EXPECT_EQ(result.Failure().reason, "error: make the program invalid");
-}
-
-TEST_F(HlslASTPrinterTest, UnsupportedExtension) {
-    Enable(Source{{12, 34}}, wgsl::Extension::kChromiumExperimentalFramebufferFetch);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_FALSE(gen.Generate());
-    EXPECT_EQ(
-        gen.Diagnostics().Str(),
-        R"(12:34 error: HLSL backend does not support extension 'chromium_experimental_framebuffer_fetch')");
-}
-
-TEST_F(HlslASTPrinterTest, RequiresDirective) {
-    Require(wgsl::LanguageFeature::kReadonlyAndReadwriteStorageTextures);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "");
-}
-
-TEST_F(HlslASTPrinterTest, Generate) {
-    Func("my_func", {}, ty.void_(), {});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(void my_func() {
-}
-)");
-}
-
-struct HlslBuiltinData {
-    core::BuiltinValue builtin;
-    const char* attribute_name;
-};
-inline std::ostream& operator<<(std::ostream& out, HlslBuiltinData data) {
-    StringStream str;
-    str << data.builtin;
-    out << str.str();
-    return out;
-}
-using HlslBuiltinConversionTest = TestParamHelper<HlslBuiltinData>;
-TEST_P(HlslBuiltinConversionTest, Emit) {
-    auto params = GetParam();
-    ASTPrinter& gen = Build();
-
-    EXPECT_EQ(gen.builtin_to_attribute(params.builtin), std::string(params.attribute_name));
-}
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest,
-    HlslBuiltinConversionTest,
-    testing::Values(HlslBuiltinData{core::BuiltinValue::kPosition, "SV_Position"},
-                    HlslBuiltinData{core::BuiltinValue::kVertexIndex, "SV_VertexID"},
-                    HlslBuiltinData{core::BuiltinValue::kInstanceIndex, "SV_InstanceID"},
-                    HlslBuiltinData{core::BuiltinValue::kFrontFacing, "SV_IsFrontFace"},
-                    HlslBuiltinData{core::BuiltinValue::kFragDepth, "SV_Depth"},
-                    HlslBuiltinData{core::BuiltinValue::kLocalInvocationId, "SV_GroupThreadID"},
-                    HlslBuiltinData{core::BuiltinValue::kLocalInvocationIndex, "SV_GroupIndex"},
-                    HlslBuiltinData{core::BuiltinValue::kGlobalInvocationId, "SV_DispatchThreadID"},
-                    HlslBuiltinData{core::BuiltinValue::kWorkgroupId, "SV_GroupID"},
-                    HlslBuiltinData{core::BuiltinValue::kSampleIndex, "SV_SampleIndex"},
-                    HlslBuiltinData{core::BuiltinValue::kSampleMask, "SV_Coverage"}));
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/binary_test.cc b/src/tint/lang/hlsl/writer/ast_printer/binary_test.cc
deleted file mode 100644
index 5bee9f9..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/binary_test.cc
+++ /dev/null
@@ -1,674 +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/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using HlslASTPrinterTest_Binary = TestHelper;
-
-struct BinaryData {
-    const char* result;
-    core::BinaryOp op;
-
-    enum Types { All = 0b11, Integer = 0b10, Float = 0b01 };
-    Types valid_for = Types::All;
-};
-inline std::ostream& operator<<(std::ostream& out, BinaryData data) {
-    StringStream str;
-    str << data.op;
-    out << str.str();
-    return out;
-}
-
-using HlslBinaryTest = TestParamHelper<BinaryData>;
-TEST_P(HlslBinaryTest, Emit_f32) {
-    auto params = GetParam();
-
-    if ((params.valid_for & BinaryData::Types::Float) == 0) {
-        return;
-    }
-
-    // Skip ops that are illegal for this type
-    if (params.op == core::BinaryOp::kAnd || params.op == core::BinaryOp::kOr ||
-        params.op == core::BinaryOp::kXor || params.op == core::BinaryOp::kShiftLeft ||
-        params.op == core::BinaryOp::kShiftRight) {
-        return;
-    }
-
-    GlobalVar("left", ty.f32(), core::AddressSpace::kPrivate);
-    GlobalVar("right", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* left = Expr("left");
-    auto* right = Expr("right");
-
-    auto* expr = create<ast::BinaryExpression>(params.op, left, right);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-TEST_P(HlslBinaryTest, Emit_f16) {
-    auto params = GetParam();
-
-    if ((params.valid_for & BinaryData::Types::Float) == 0) {
-        return;
-    }
-
-    // Skip ops that are illegal for this type
-    if (params.op == core::BinaryOp::kAnd || params.op == core::BinaryOp::kOr ||
-        params.op == core::BinaryOp::kXor || params.op == core::BinaryOp::kShiftLeft ||
-        params.op == core::BinaryOp::kShiftRight) {
-        return;
-    }
-
-    Enable(wgsl::Extension::kF16);
-
-    GlobalVar("left", ty.f16(), core::AddressSpace::kPrivate);
-    GlobalVar("right", ty.f16(), core::AddressSpace::kPrivate);
-
-    auto* left = Expr("left");
-    auto* right = Expr("right");
-
-    auto* expr = create<ast::BinaryExpression>(params.op, left, right);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-TEST_P(HlslBinaryTest, Emit_u32) {
-    auto params = GetParam();
-
-    if ((params.valid_for & BinaryData::Types::Integer) == 0) {
-        return;
-    }
-
-    GlobalVar("left", ty.u32(), core::AddressSpace::kPrivate);
-    GlobalVar("right", ty.u32(), core::AddressSpace::kPrivate);
-
-    auto* left = Expr("left");
-    auto* right = Expr("right");
-
-    auto* expr = create<ast::BinaryExpression>(params.op, left, right);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-TEST_P(HlslBinaryTest, Emit_i32) {
-    auto params = GetParam();
-
-    if ((params.valid_for & BinaryData::Types::Integer) == 0) {
-        return;
-    }
-
-    // Skip ops that are illegal for this type
-    if (params.op == core::BinaryOp::kShiftLeft || params.op == core::BinaryOp::kShiftRight) {
-        return;
-    }
-
-    GlobalVar("left", ty.i32(), core::AddressSpace::kPrivate);
-    GlobalVar("right", ty.i32(), core::AddressSpace::kPrivate);
-
-    auto* left = Expr("left");
-    auto* right = Expr("right");
-
-    auto* expr = create<ast::BinaryExpression>(params.op, left, right);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), params.result);
-}
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest,
-    HlslBinaryTest,
-    testing::Values(BinaryData{"(left & right)", core::BinaryOp::kAnd},
-                    BinaryData{"(left | right)", core::BinaryOp::kOr},
-                    BinaryData{"(left ^ right)", core::BinaryOp::kXor},
-                    BinaryData{"(left == right)", core::BinaryOp::kEqual},
-                    BinaryData{"(left != right)", core::BinaryOp::kNotEqual},
-                    BinaryData{"(left < right)", core::BinaryOp::kLessThan},
-                    BinaryData{"(left > right)", core::BinaryOp::kGreaterThan},
-                    BinaryData{"(left <= right)", core::BinaryOp::kLessThanEqual},
-                    BinaryData{"(left >= right)", core::BinaryOp::kGreaterThanEqual},
-                    BinaryData{"(left << right)", core::BinaryOp::kShiftLeft},
-                    BinaryData{"(left >> right)", core::BinaryOp::kShiftRight},
-                    BinaryData{"(left + right)", core::BinaryOp::kAdd},
-                    BinaryData{"(left - right)", core::BinaryOp::kSubtract},
-                    BinaryData{"(left * right)", core::BinaryOp::kMultiply},
-                    // NOTE: Integer divide covered by DivOrModBy* tests below
-                    BinaryData{"(left / right)", core::BinaryOp::kDivide, BinaryData::Types::Float},
-                    // NOTE: Integer modulo covered by DivOrModBy* tests below
-                    BinaryData{"(left % right)", core::BinaryOp::kModulo,
-                               BinaryData::Types::Float}));
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_VectorScalar_f32) {
-    auto* lhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
-    auto* rhs = Expr(1_f);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(1.0f).xxx");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_VectorScalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* lhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
-    auto* rhs = Expr(1_h);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(float16_t(1.0h)).xxx");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_ScalarVector_f32) {
-    auto* lhs = Expr(1_f);
-    auto* rhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(1.0f).xxx");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_ScalarVector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* lhs = Expr(1_h);
-    auto* rhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(float16_t(1.0h)).xxx");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_MatrixScalar_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
-    auto* lhs = Expr("mat");
-    auto* rhs = Expr(1_f);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(mat * 1.0f)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_MatrixScalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    GlobalVar("mat", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
-    auto* lhs = Expr("mat");
-    auto* rhs = Expr(1_h);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(mat * float16_t(1.0h))");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_ScalarMatrix_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
-    auto* lhs = Expr(1_f);
-    auto* rhs = Expr("mat");
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(1.0f * mat)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_ScalarMatrix_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    GlobalVar("mat", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
-    auto* lhs = Expr(1_h);
-    auto* rhs = Expr("mat");
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(float16_t(1.0h) * mat)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_MatrixVector_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
-    auto* lhs = Expr("mat");
-    auto* rhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "mul((1.0f).xxx, mat)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_MatrixVector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    GlobalVar("mat", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
-    auto* lhs = Expr("mat");
-    auto* rhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "mul((float16_t(1.0h)).xxx, mat)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_VectorMatrix_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
-    auto* lhs = Call<vec3<f32>>(1_f, 1_f, 1_f);
-    auto* rhs = Expr("mat");
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "mul(mat, (1.0f).xxx)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_VectorMatrix_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    GlobalVar("mat", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
-    auto* lhs = Call<vec3<f16>>(1_h, 1_h, 1_h);
-    auto* rhs = Expr("mat");
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, lhs, rhs);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "mul(mat, (float16_t(1.0h)).xxx)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_MatrixMatrix_f32) {
-    GlobalVar("lhs", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("rhs", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "mul(rhs, lhs)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Multiply_MatrixMatrix_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    GlobalVar("lhs", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
-    GlobalVar("rhs", ty.mat3x3<f16>(), core::AddressSpace::kPrivate);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    EXPECT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "mul(rhs, lhs)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Logical_And) {
-    GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(tint_tmp)");
-    EXPECT_EQ(gen.Result(), R"(bool tint_tmp = a;
-if (tint_tmp) {
-  tint_tmp = b;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Logical_Multi) {
-    // (a && b) || (c || d)
-    GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("d", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* expr = create<ast::BinaryExpression>(
-        core::BinaryOp::kLogicalOr,
-        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
-        create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("c"), Expr("d")));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(tint_tmp)");
-    EXPECT_EQ(gen.Result(), R"(bool tint_tmp_1 = a;
-if (tint_tmp_1) {
-  tint_tmp_1 = b;
-}
-bool tint_tmp = (tint_tmp_1);
-if (!tint_tmp) {
-  bool tint_tmp_2 = c;
-  if (!tint_tmp_2) {
-    tint_tmp_2 = d;
-  }
-  tint_tmp = (tint_tmp_2);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Logical_Or) {
-    GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* expr = create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(tint_tmp)");
-    EXPECT_EQ(gen.Result(), R"(bool tint_tmp = a;
-if (!tint_tmp) {
-  tint_tmp = b;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, If_WithLogical) {
-    // if (a && b) {
-    //   return 1i;
-    // } else if (b || c) {
-    //   return 2i;
-    // } else {
-    //   return 3i;
-    // }
-
-    GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* expr =
-        If(create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
-           Block(Return(1_i)),
-           Else(If(create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
-                   Block(Return(2_i)), Else(Block(Return(3_i))))));
-    Func("func", tint::Empty, ty.i32(), Vector{WrapInStatement(expr)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(bool tint_tmp = a;
-if (tint_tmp) {
-  tint_tmp = b;
-}
-if ((tint_tmp)) {
-  return 1;
-} else {
-  bool tint_tmp_1 = b;
-  if (!tint_tmp_1) {
-    tint_tmp_1 = c;
-  }
-  if ((tint_tmp_1)) {
-    return 2;
-  } else {
-    return 3;
-  }
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Return_WithLogical) {
-    // return (a && b) || c;
-
-    GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* expr = Return(create<ast::BinaryExpression>(
-        core::BinaryOp::kLogicalOr,
-        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
-        Expr("c")));
-    Func("func", tint::Empty, ty.bool_(), Vector{WrapInStatement(expr)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(bool tint_tmp_1 = a;
-if (tint_tmp_1) {
-  tint_tmp_1 = b;
-}
-bool tint_tmp = (tint_tmp_1);
-if (!tint_tmp) {
-  tint_tmp = c;
-}
-return (tint_tmp);
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Assign_WithLogical) {
-    // a = (b || c) && d;
-
-    GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("d", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* expr =
-        Assign(Expr("a"),
-               create<ast::BinaryExpression>(
-                   core::BinaryOp::kLogicalAnd,
-                   create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("c")),
-                   Expr("d")));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(bool tint_tmp_1 = b;
-if (!tint_tmp_1) {
-  tint_tmp_1 = c;
-}
-bool tint_tmp = (tint_tmp_1);
-if (tint_tmp) {
-  tint_tmp = d;
-}
-a = (tint_tmp);
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Decl_WithLogical) {
-    // var a : bool = (b && c) || d;
-
-    GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("d", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* var =
-        Var("a", ty.bool_(), core::AddressSpace::kUndefined,
-            create<ast::BinaryExpression>(
-                core::BinaryOp::kLogicalOr,
-                create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("b"), Expr("c")),
-                Expr("d")));
-
-    auto* decl = Decl(var);
-    WrapInFunction(decl);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(decl)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(bool tint_tmp_1 = b;
-if (tint_tmp_1) {
-  tint_tmp_1 = c;
-}
-bool tint_tmp = (tint_tmp_1);
-if (!tint_tmp) {
-  tint_tmp = d;
-}
-bool a = (tint_tmp);
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Binary, Call_WithLogical) {
-    // foo(a && b, c || d, (a || c) && (b || d))
-
-    Func("foo",
-         Vector{
-             Param(Sym(), ty.bool_()),
-             Param(Sym(), ty.bool_()),
-             Param(Sym(), ty.bool_()),
-         },
-         ty.void_(), tint::Empty, tint::Empty);
-    GlobalVar("a", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("b", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("c", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("d", ty.bool_(), core::AddressSpace::kPrivate);
-
-    Vector params{
-        create<ast::BinaryExpression>(core::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
-        create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("c"), Expr("d")),
-        create<ast::BinaryExpression>(
-            core::BinaryOp::kLogicalAnd,
-            create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("a"), Expr("c")),
-            create<ast::BinaryExpression>(core::BinaryOp::kLogicalOr, Expr("b"), Expr("d"))),
-    };
-
-    auto* expr = CallStmt(Call("foo", params));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(expr)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(bool tint_tmp = a;
-if (tint_tmp) {
-  tint_tmp = b;
-}
-bool tint_tmp_1 = c;
-if (!tint_tmp_1) {
-  tint_tmp_1 = d;
-}
-bool tint_tmp_3 = a;
-if (!tint_tmp_3) {
-  tint_tmp_3 = c;
-}
-bool tint_tmp_2 = (tint_tmp_3);
-if (tint_tmp_2) {
-  bool tint_tmp_4 = b;
-  if (!tint_tmp_4) {
-    tint_tmp_4 = d;
-  }
-  tint_tmp_2 = (tint_tmp_4);
-}
-foo((tint_tmp), (tint_tmp_1), (tint_tmp_2));
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/bitcast_test.cc b/src/tint/lang/hlsl/writer/ast_printer/bitcast_test.cc
deleted file mode 100644
index bb89d6f..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/bitcast_test.cc
+++ /dev/null
@@ -1,210 +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/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-#include "gmock/gmock.h"
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Bitcast = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Bitcast, EmitExpression_Bitcast_Float) {
-    auto* a = Let("a", Expr(1_i));
-    auto* bitcast = Bitcast<f32>(Expr("a"));
-    WrapInFunction(a, bitcast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "asfloat(a)");
-}
-
-TEST_F(HlslASTPrinterTest_Bitcast, EmitExpression_Bitcast_Int) {
-    auto* a = Let("a", Expr(1_u));
-    auto* bitcast = Bitcast<i32>(Expr("a"));
-    WrapInFunction(a, bitcast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "asint(a)");
-}
-
-TEST_F(HlslASTPrinterTest_Bitcast, EmitExpression_Bitcast_Uint) {
-    auto* a = Let("a", Expr(1_i));
-    auto* bitcast = Bitcast<u32>(Expr("a"));
-    WrapInFunction(a, bitcast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, bitcast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "asuint(a)");
-}
-
-TEST_F(HlslASTPrinterTest_Bitcast, EmitExpression_Bitcast_F16_Vec2) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* a = Let("a", Call<vec2<f16>>(1_h, 2_h));
-    auto* b = Let("b", Bitcast<i32>(Expr("a")));
-    auto* c = Let("c", Bitcast<vec2<f16>>(Expr("b")));
-    auto* d = Let("d", Bitcast<f32>(Expr("c")));
-    auto* e = Let("e", Bitcast<vec2<f16>>(Expr("d")));
-    auto* f = Let("f", Bitcast<u32>(Expr("e")));
-    auto* g = Let("g", Bitcast<vec2<f16>>(Expr("f")));
-    WrapInFunction(a, b, c, d, e, f, g);
-
-    ASTPrinter& gen = Build();
-
-    gen.Generate();
-    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
-    EXPECT_EQ(gen.Result(), R"(int tint_bitcast_from_f16(vector<float16_t, 2> src) {
-  uint2 r = f32tof16(float2(src));
-  return asint(uint((r.x & 0xffff) | ((r.y & 0xffff) << 16)));
-}
-
-vector<float16_t, 2> tint_bitcast_to_f16(int src) {
-  uint v = asuint(src);
-  float t_low = f16tof32(v & 0xffff);
-  float t_high = f16tof32((v >> 16) & 0xffff);
-  return vector<float16_t, 2>(t_low.x, t_high.x);
-}
-
-float tint_bitcast_from_f16_1(vector<float16_t, 2> src) {
-  uint2 r = f32tof16(float2(src));
-  return asfloat(uint((r.x & 0xffff) | ((r.y & 0xffff) << 16)));
-}
-
-vector<float16_t, 2> tint_bitcast_to_f16_1(float src) {
-  uint v = asuint(src);
-  float t_low = f16tof32(v & 0xffff);
-  float t_high = f16tof32((v >> 16) & 0xffff);
-  return vector<float16_t, 2>(t_low.x, t_high.x);
-}
-
-uint tint_bitcast_from_f16_2(vector<float16_t, 2> src) {
-  uint2 r = f32tof16(float2(src));
-  return asuint(uint((r.x & 0xffff) | ((r.y & 0xffff) << 16)));
-}
-
-vector<float16_t, 2> tint_bitcast_to_f16_2(uint src) {
-  uint v = asuint(src);
-  float t_low = f16tof32(v & 0xffff);
-  float t_high = f16tof32((v >> 16) & 0xffff);
-  return vector<float16_t, 2>(t_low.x, t_high.x);
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  vector<float16_t, 2> a = vector<float16_t, 2>(float16_t(1.0h), float16_t(2.0h));
-  int b = tint_bitcast_from_f16(a);
-  vector<float16_t, 2> c = tint_bitcast_to_f16(b);
-  float d = tint_bitcast_from_f16_1(c);
-  vector<float16_t, 2> e = tint_bitcast_to_f16_1(d);
-  uint f = tint_bitcast_from_f16_2(e);
-  vector<float16_t, 2> g = tint_bitcast_to_f16_2(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Bitcast, EmitExpression_Bitcast_F16_Vec4) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* a = Let("a", Call<vec4<f16>>(1_h, 2_h, 3_h, 4_h));
-    auto* b = Let("b", Bitcast<vec2<i32>>(Expr("a")));
-    auto* c = Let("c", Bitcast<vec4<f16>>(Expr("b")));
-    auto* d = Let("d", Bitcast<vec2<f32>>(Expr("c")));
-    auto* e = Let("e", Bitcast<vec4<f16>>(Expr("d")));
-    auto* f = Let("f", Bitcast<vec2<u32>>(Expr("e")));
-    auto* g = Let("g", Bitcast<vec4<f16>>(Expr("f")));
-    WrapInFunction(a, b, c, d, e, f, g);
-
-    ASTPrinter& gen = Build();
-
-    gen.Generate();
-    EXPECT_THAT(gen.Diagnostics(), testing::IsEmpty());
-    EXPECT_EQ(gen.Result(), R"(int2 tint_bitcast_from_f16(vector<float16_t, 4> src) {
-  uint4 r = f32tof16(float4(src));
-  return asint(uint2((r.x & 0xffff) | ((r.y & 0xffff) << 16), (r.z & 0xffff) | ((r.w & 0xffff) << 16)));
-}
-
-vector<float16_t, 4> tint_bitcast_to_f16(int2 src) {
-  uint2 v = asuint(src);
-  float2 t_low = f16tof32(v & 0xffff);
-  float2 t_high = f16tof32((v >> 16) & 0xffff);
-  return vector<float16_t, 4>(t_low.x, t_high.x, t_low.y, t_high.y);
-}
-
-float2 tint_bitcast_from_f16_1(vector<float16_t, 4> src) {
-  uint4 r = f32tof16(float4(src));
-  return asfloat(uint2((r.x & 0xffff) | ((r.y & 0xffff) << 16), (r.z & 0xffff) | ((r.w & 0xffff) << 16)));
-}
-
-vector<float16_t, 4> tint_bitcast_to_f16_1(float2 src) {
-  uint2 v = asuint(src);
-  float2 t_low = f16tof32(v & 0xffff);
-  float2 t_high = f16tof32((v >> 16) & 0xffff);
-  return vector<float16_t, 4>(t_low.x, t_high.x, t_low.y, t_high.y);
-}
-
-uint2 tint_bitcast_from_f16_2(vector<float16_t, 4> src) {
-  uint4 r = f32tof16(float4(src));
-  return asuint(uint2((r.x & 0xffff) | ((r.y & 0xffff) << 16), (r.z & 0xffff) | ((r.w & 0xffff) << 16)));
-}
-
-vector<float16_t, 4> tint_bitcast_to_f16_2(uint2 src) {
-  uint2 v = asuint(src);
-  float2 t_low = f16tof32(v & 0xffff);
-  float2 t_high = f16tof32((v >> 16) & 0xffff);
-  return vector<float16_t, 4>(t_low.x, t_high.x, t_low.y, t_high.y);
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  vector<float16_t, 4> a = vector<float16_t, 4>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h), float16_t(4.0h));
-  int2 b = tint_bitcast_from_f16(a);
-  vector<float16_t, 4> c = tint_bitcast_to_f16(b);
-  float2 d = tint_bitcast_from_f16_1(c);
-  vector<float16_t, 4> e = tint_bitcast_to_f16_1(d);
-  uint2 f = tint_bitcast_from_f16_2(e);
-  vector<float16_t, 4> g = tint_bitcast_to_f16_2(f);
-  return;
-}
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/block_test.cc b/src/tint/lang/hlsl/writer/ast_printer/block_test.cc
deleted file mode 100644
index faa847b..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/block_test.cc
+++ /dev/null
@@ -1,51 +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/hlsl/writer/ast_printer/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Block = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Block, Emit_Block) {
-    auto* b = Block(Return());
-    WrapInFunction(b);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    return;
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/break_test.cc b/src/tint/lang/hlsl/writer/ast_printer/break_test.cc
deleted file mode 100644
index 61b39d4..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/break_test.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Break = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Break, Emit_Break) {
-    auto* b = create<ast::BreakStatement>();
-    WrapInFunction(Loop(Block(b)));
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(b)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  break;\n");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/builtin_test.cc b/src/tint/lang/hlsl/writer/ast_printer/builtin_test.cc
deleted file mode 100644
index 770f7ce..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/builtin_test.cc
+++ /dev/null
@@ -1,1538 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-#include "src/tint/lang/wgsl/sem/call.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using ::testing::HasSubstr;
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using HlslASTPrinterTest_Builtin = TestHelper;
-
-enum class CallParamType {
-    kF32,
-    kU32,
-    kBool,
-    kF16,
-};
-
-struct BuiltinData {
-    wgsl::BuiltinFn builtin;
-    CallParamType type;
-    const char* hlsl_name;
-};
-inline std::ostream& operator<<(std::ostream& out, BuiltinData data) {
-    out << data.hlsl_name << "<";
-    switch (data.type) {
-        case CallParamType::kF32:
-            out << "f32";
-            break;
-        case CallParamType::kU32:
-            out << "u32";
-            break;
-        case CallParamType::kBool:
-            out << "bool";
-            break;
-        case CallParamType::kF16:
-            out << "f16";
-            break;
-    }
-    out << ">";
-    return out;
-}
-
-const ast::CallExpression* GenerateCall(wgsl::BuiltinFn builtin,
-                                        CallParamType type,
-                                        ProgramBuilder* builder) {
-    std::string name;
-    StringStream str;
-    str << name << builtin;
-    switch (builtin) {
-        case wgsl::BuiltinFn::kAcos:
-        case wgsl::BuiltinFn::kAsin:
-        case wgsl::BuiltinFn::kAtan:
-        case wgsl::BuiltinFn::kCeil:
-        case wgsl::BuiltinFn::kCos:
-        case wgsl::BuiltinFn::kCosh:
-        case wgsl::BuiltinFn::kDpdx:
-        case wgsl::BuiltinFn::kDpdxCoarse:
-        case wgsl::BuiltinFn::kDpdxFine:
-        case wgsl::BuiltinFn::kDpdy:
-        case wgsl::BuiltinFn::kDpdyCoarse:
-        case wgsl::BuiltinFn::kDpdyFine:
-        case wgsl::BuiltinFn::kExp:
-        case wgsl::BuiltinFn::kExp2:
-        case wgsl::BuiltinFn::kFloor:
-        case wgsl::BuiltinFn::kFract:
-        case wgsl::BuiltinFn::kFwidth:
-        case wgsl::BuiltinFn::kFwidthCoarse:
-        case wgsl::BuiltinFn::kFwidthFine:
-        case wgsl::BuiltinFn::kInverseSqrt:
-        case wgsl::BuiltinFn::kLength:
-        case wgsl::BuiltinFn::kLog:
-        case wgsl::BuiltinFn::kLog2:
-        case wgsl::BuiltinFn::kNormalize:
-        case wgsl::BuiltinFn::kRound:
-        case wgsl::BuiltinFn::kSin:
-        case wgsl::BuiltinFn::kSinh:
-        case wgsl::BuiltinFn::kSqrt:
-        case wgsl::BuiltinFn::kTan:
-        case wgsl::BuiltinFn::kTanh:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2");
-            } else {
-                return builder->Call(str.str(), "f2");
-            }
-        case wgsl::BuiltinFn::kLdexp:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "i2");
-            } else {
-                return builder->Call(str.str(), "f2", "i2");
-            }
-        case wgsl::BuiltinFn::kAtan2:
-        case wgsl::BuiltinFn::kDot:
-        case wgsl::BuiltinFn::kDistance:
-        case wgsl::BuiltinFn::kPow:
-        case wgsl::BuiltinFn::kReflect:
-        case wgsl::BuiltinFn::kStep:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2");
-            } else {
-                return builder->Call(str.str(), "f2", "f2");
-            }
-        case wgsl::BuiltinFn::kCross:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h3", "h3");
-            } else {
-                return builder->Call(str.str(), "f3", "f3");
-            }
-        case wgsl::BuiltinFn::kFma:
-        case wgsl::BuiltinFn::kMix:
-        case wgsl::BuiltinFn::kFaceForward:
-        case wgsl::BuiltinFn::kSmoothstep:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2", "h2");
-            } else {
-                return builder->Call(str.str(), "f2", "f2", "f2");
-            }
-        case wgsl::BuiltinFn::kAll:
-        case wgsl::BuiltinFn::kAny:
-            return builder->Call(str.str(), "b2");
-        case wgsl::BuiltinFn::kAbs:
-            if (type == CallParamType::kF32) {
-                return builder->Call(str.str(), "f2");
-            } else if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2");
-            } else {
-                return builder->Call(str.str(), "u2");
-            }
-        case wgsl::BuiltinFn::kCountOneBits:
-        case wgsl::BuiltinFn::kReverseBits:
-            return builder->Call(str.str(), "u2");
-        case wgsl::BuiltinFn::kMax:
-        case wgsl::BuiltinFn::kMin:
-            if (type == CallParamType::kF32) {
-                return builder->Call(str.str(), "f2", "f2");
-            } else if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2");
-            } else {
-                return builder->Call(str.str(), "u2", "u2");
-            }
-        case wgsl::BuiltinFn::kClamp:
-            if (type == CallParamType::kF32) {
-                return builder->Call(str.str(), "f2", "f2", "f2");
-            } else if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2", "h2");
-            } else {
-                return builder->Call(str.str(), "u2", "u2", "u2");
-            }
-        case wgsl::BuiltinFn::kSelect:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "h2", "h2", "b2");
-            } else {
-                return builder->Call(str.str(), "f2", "f2", "b2");
-            }
-        case wgsl::BuiltinFn::kDeterminant:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "hm2x2");
-            } else {
-                return builder->Call(str.str(), "m2x2");
-            }
-        case wgsl::BuiltinFn::kTranspose:
-            if (type == CallParamType::kF16) {
-                return builder->Call(str.str(), "hm3x2");
-            } else {
-                return builder->Call(str.str(), "m3x2");
-            }
-        default:
-            break;
-    }
-    return nullptr;
-}
-
-using HlslBuiltinTest = TestParamHelper<BuiltinData>;
-TEST_P(HlslBuiltinTest, Emit) {
-    auto param = GetParam();
-
-    if (param.type == CallParamType::kF16) {
-        Enable(wgsl::Extension::kF16);
-
-        GlobalVar("h2", ty.vec2<f16>(), core::AddressSpace::kPrivate);
-        GlobalVar("h3", ty.vec3<f16>(), core::AddressSpace::kPrivate);
-        GlobalVar("hm2x2", ty.mat2x2<f16>(), core::AddressSpace::kPrivate);
-        GlobalVar("hm3x2", ty.mat3x2<f16>(), core::AddressSpace::kPrivate);
-    }
-
-    GlobalVar("f2", ty.vec2<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("f3", ty.vec3<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("u2", ty.vec2<u32>(), core::AddressSpace::kPrivate);
-    GlobalVar("i2", ty.vec2<i32>(), core::AddressSpace::kPrivate);
-    GlobalVar("b2", ty.vec2<bool>(), core::AddressSpace::kPrivate);
-    GlobalVar("m2x2", ty.mat2x2<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("m3x2", ty.mat3x2<f32>(), core::AddressSpace::kPrivate);
-
-    auto* call = GenerateCall(param.builtin, param.type, this);
-    ASSERT_NE(nullptr, call) << "Unhandled builtin";
-    Func("func", tint::Empty, ty.void_(),
-         Vector{
-             Assign(Phony(), call),
-         },
-         Vector{create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    auto* sem = program->Sem().Get<sem::Call>(call);
-    ASSERT_NE(sem, nullptr);
-    auto* target = sem->Target();
-    ASSERT_NE(target, nullptr);
-    auto* builtin = target->As<sem::BuiltinFn>();
-    ASSERT_NE(builtin, nullptr);
-
-    EXPECT_EQ(gen.generate_builtin_name(builtin), param.hlsl_name);
-}
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest_Builtin,
-    HlslBuiltinTest,
-    testing::Values(/* Logical built-in */
-                    BuiltinData{wgsl::BuiltinFn::kAll, CallParamType::kBool, "all"},
-                    BuiltinData{wgsl::BuiltinFn::kAny, CallParamType::kBool, "any"},
-                    /* Float built-in */
-                    BuiltinData{wgsl::BuiltinFn::kAbs, CallParamType::kF32, "abs"},
-                    BuiltinData{wgsl::BuiltinFn::kAbs, CallParamType::kF16, "abs"},
-                    BuiltinData{wgsl::BuiltinFn::kAcos, CallParamType::kF32, "acos"},
-                    BuiltinData{wgsl::BuiltinFn::kAcos, CallParamType::kF16, "acos"},
-                    BuiltinData{wgsl::BuiltinFn::kAsin, CallParamType::kF32, "asin"},
-                    BuiltinData{wgsl::BuiltinFn::kAsin, CallParamType::kF16, "asin"},
-                    BuiltinData{wgsl::BuiltinFn::kAtan, CallParamType::kF32, "atan"},
-                    BuiltinData{wgsl::BuiltinFn::kAtan, CallParamType::kF16, "atan"},
-                    BuiltinData{wgsl::BuiltinFn::kAtan2, CallParamType::kF32, "atan2"},
-                    BuiltinData{wgsl::BuiltinFn::kAtan2, CallParamType::kF16, "atan2"},
-                    BuiltinData{wgsl::BuiltinFn::kCeil, CallParamType::kF32, "ceil"},
-                    BuiltinData{wgsl::BuiltinFn::kCeil, CallParamType::kF16, "ceil"},
-                    BuiltinData{wgsl::BuiltinFn::kClamp, CallParamType::kF32, "clamp"},
-                    BuiltinData{wgsl::BuiltinFn::kClamp, CallParamType::kF16, "clamp"},
-                    BuiltinData{wgsl::BuiltinFn::kCos, CallParamType::kF32, "cos"},
-                    BuiltinData{wgsl::BuiltinFn::kCos, CallParamType::kF16, "cos"},
-                    BuiltinData{wgsl::BuiltinFn::kCosh, CallParamType::kF32, "cosh"},
-                    BuiltinData{wgsl::BuiltinFn::kCosh, CallParamType::kF16, "cosh"},
-                    BuiltinData{wgsl::BuiltinFn::kCross, CallParamType::kF32, "cross"},
-                    BuiltinData{wgsl::BuiltinFn::kCross, CallParamType::kF16, "cross"},
-                    BuiltinData{wgsl::BuiltinFn::kDistance, CallParamType::kF32, "distance"},
-                    BuiltinData{wgsl::BuiltinFn::kDistance, CallParamType::kF16, "distance"},
-                    BuiltinData{wgsl::BuiltinFn::kExp, CallParamType::kF32, "exp"},
-                    BuiltinData{wgsl::BuiltinFn::kExp, CallParamType::kF16, "exp"},
-                    BuiltinData{wgsl::BuiltinFn::kExp2, CallParamType::kF32, "exp2"},
-                    BuiltinData{wgsl::BuiltinFn::kExp2, CallParamType::kF16, "exp2"},
-                    BuiltinData{wgsl::BuiltinFn::kFaceForward, CallParamType::kF32, "faceforward"},
-                    BuiltinData{wgsl::BuiltinFn::kFaceForward, CallParamType::kF16, "faceforward"},
-                    BuiltinData{wgsl::BuiltinFn::kFloor, CallParamType::kF32, "floor"},
-                    BuiltinData{wgsl::BuiltinFn::kFloor, CallParamType::kF16, "floor"},
-                    BuiltinData{wgsl::BuiltinFn::kFma, CallParamType::kF32, "mad"},
-                    BuiltinData{wgsl::BuiltinFn::kFma, CallParamType::kF16, "mad"},
-                    BuiltinData{wgsl::BuiltinFn::kFract, CallParamType::kF32, "frac"},
-                    BuiltinData{wgsl::BuiltinFn::kFract, CallParamType::kF16, "frac"},
-                    BuiltinData{wgsl::BuiltinFn::kInverseSqrt, CallParamType::kF32, "rsqrt"},
-                    BuiltinData{wgsl::BuiltinFn::kInverseSqrt, CallParamType::kF16, "rsqrt"},
-                    BuiltinData{wgsl::BuiltinFn::kLdexp, CallParamType::kF32, "ldexp"},
-                    BuiltinData{wgsl::BuiltinFn::kLdexp, CallParamType::kF16, "ldexp"},
-                    BuiltinData{wgsl::BuiltinFn::kLength, CallParamType::kF32, "length"},
-                    BuiltinData{wgsl::BuiltinFn::kLength, CallParamType::kF16, "length"},
-                    BuiltinData{wgsl::BuiltinFn::kLog, CallParamType::kF32, "log"},
-                    BuiltinData{wgsl::BuiltinFn::kLog, CallParamType::kF16, "log"},
-                    BuiltinData{wgsl::BuiltinFn::kLog2, CallParamType::kF32, "log2"},
-                    BuiltinData{wgsl::BuiltinFn::kLog2, CallParamType::kF16, "log2"},
-                    BuiltinData{wgsl::BuiltinFn::kMax, CallParamType::kF32, "max"},
-                    BuiltinData{wgsl::BuiltinFn::kMax, CallParamType::kF16, "max"},
-                    BuiltinData{wgsl::BuiltinFn::kMin, CallParamType::kF32, "min"},
-                    BuiltinData{wgsl::BuiltinFn::kMin, CallParamType::kF16, "min"},
-                    BuiltinData{wgsl::BuiltinFn::kMix, CallParamType::kF32, "lerp"},
-                    BuiltinData{wgsl::BuiltinFn::kMix, CallParamType::kF16, "lerp"},
-                    BuiltinData{wgsl::BuiltinFn::kNormalize, CallParamType::kF32, "normalize"},
-                    BuiltinData{wgsl::BuiltinFn::kNormalize, CallParamType::kF16, "normalize"},
-                    BuiltinData{wgsl::BuiltinFn::kPow, CallParamType::kF32, "pow"},
-                    BuiltinData{wgsl::BuiltinFn::kPow, CallParamType::kF16, "pow"},
-                    BuiltinData{wgsl::BuiltinFn::kReflect, CallParamType::kF32, "reflect"},
-                    BuiltinData{wgsl::BuiltinFn::kReflect, CallParamType::kF16, "reflect"},
-                    BuiltinData{wgsl::BuiltinFn::kSin, CallParamType::kF32, "sin"},
-                    BuiltinData{wgsl::BuiltinFn::kSin, CallParamType::kF16, "sin"},
-                    BuiltinData{wgsl::BuiltinFn::kSinh, CallParamType::kF32, "sinh"},
-                    BuiltinData{wgsl::BuiltinFn::kSinh, CallParamType::kF16, "sinh"},
-                    BuiltinData{wgsl::BuiltinFn::kSmoothstep, CallParamType::kF32, "smoothstep"},
-                    BuiltinData{wgsl::BuiltinFn::kSmoothstep, CallParamType::kF16, "smoothstep"},
-                    BuiltinData{wgsl::BuiltinFn::kSqrt, CallParamType::kF32, "sqrt"},
-                    BuiltinData{wgsl::BuiltinFn::kSqrt, CallParamType::kF16, "sqrt"},
-                    BuiltinData{wgsl::BuiltinFn::kStep, CallParamType::kF32, "step"},
-                    BuiltinData{wgsl::BuiltinFn::kStep, CallParamType::kF16, "step"},
-                    BuiltinData{wgsl::BuiltinFn::kTan, CallParamType::kF32, "tan"},
-                    BuiltinData{wgsl::BuiltinFn::kTan, CallParamType::kF16, "tan"},
-                    BuiltinData{wgsl::BuiltinFn::kTanh, CallParamType::kF32, "tanh"},
-                    BuiltinData{wgsl::BuiltinFn::kTanh, CallParamType::kF16, "tanh"},
-                    /* Integer built-in */
-                    BuiltinData{wgsl::BuiltinFn::kAbs, CallParamType::kU32, "abs"},
-                    BuiltinData{wgsl::BuiltinFn::kClamp, CallParamType::kU32, "clamp"},
-                    BuiltinData{wgsl::BuiltinFn::kCountOneBits, CallParamType::kU32, "countbits"},
-                    BuiltinData{wgsl::BuiltinFn::kMax, CallParamType::kU32, "max"},
-                    BuiltinData{wgsl::BuiltinFn::kMin, CallParamType::kU32, "min"},
-                    BuiltinData{wgsl::BuiltinFn::kReverseBits, CallParamType::kU32, "reversebits"},
-                    BuiltinData{wgsl::BuiltinFn::kRound, CallParamType::kU32, "round"},
-                    /* Matrix built-in */
-                    BuiltinData{wgsl::BuiltinFn::kDeterminant, CallParamType::kF32, "determinant"},
-                    BuiltinData{wgsl::BuiltinFn::kDeterminant, CallParamType::kF16, "determinant"},
-                    BuiltinData{wgsl::BuiltinFn::kTranspose, CallParamType::kF32, "transpose"},
-                    BuiltinData{wgsl::BuiltinFn::kTranspose, CallParamType::kF16, "transpose"},
-                    /* Vector built-in */
-                    BuiltinData{wgsl::BuiltinFn::kDot, CallParamType::kF32, "dot"},
-                    BuiltinData{wgsl::BuiltinFn::kDot, CallParamType::kF16, "dot"},
-                    /* Derivate built-in */
-                    BuiltinData{wgsl::BuiltinFn::kDpdx, CallParamType::kF32, "ddx"},
-                    BuiltinData{wgsl::BuiltinFn::kDpdxCoarse, CallParamType::kF32, "ddx_coarse"},
-                    BuiltinData{wgsl::BuiltinFn::kDpdxFine, CallParamType::kF32, "ddx_fine"},
-                    BuiltinData{wgsl::BuiltinFn::kDpdy, CallParamType::kF32, "ddy"},
-                    BuiltinData{wgsl::BuiltinFn::kDpdyCoarse, CallParamType::kF32, "ddy_coarse"},
-                    BuiltinData{wgsl::BuiltinFn::kDpdyFine, CallParamType::kF32, "ddy_fine"},
-                    BuiltinData{wgsl::BuiltinFn::kFwidth, CallParamType::kF32, "fwidth"},
-                    BuiltinData{wgsl::BuiltinFn::kFwidthCoarse, CallParamType::kF32, "fwidth"},
-                    BuiltinData{wgsl::BuiltinFn::kFwidthFine, CallParamType::kF32, "fwidth"}));
-
-TEST_F(HlslASTPrinterTest_Builtin, Builtin_Call) {
-    auto* call = Call("dot", "param1", "param2");
-
-    GlobalVar("param1", ty.vec3<f32>(), core::AddressSpace::kPrivate);
-    GlobalVar("param2", ty.vec3<f32>(), core::AddressSpace::kPrivate);
-
-    WrapInFunction(Decl(Var("r", call)));
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "dot(param1, param2)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Select_Scalar) {
-    GlobalVar("a", Expr(1_f), core::AddressSpace::kPrivate);
-    GlobalVar("b", Expr(2_f), core::AddressSpace::kPrivate);
-    auto* call = Call("select", "a", "b", true);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(true ? b : a)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Select_Vector) {
-    GlobalVar("a", Call<vec2<i32>>(1_i, 2_i), core::AddressSpace::kPrivate);
-    GlobalVar("b", Call<vec2<i32>>(3_i, 4_i), core::AddressSpace::kPrivate);
-    auto* call = Call("select", "a", "b", Call<vec2<bool>>(true, false));
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "(bool2(true, false) ? b : a)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Runtime_Modf_Scalar_f32) {
-    WrapInFunction(Decl(Let("f", Expr(1.5_f))),  //
-                   Decl(Let("v", Call("modf", "f"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_f32 {
-  float fract;
-  float whole;
-};
-modf_result_f32 tint_modf(float param_0) {
-  modf_result_f32 result;
-  result.fract = modf(param_0, result.whole);
-  return result;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float f = 1.5f;
-  modf_result_f32 v = tint_modf(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Runtime_Modf_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("f", Expr(1.5_h))),  //
-                   Decl(Let("v", Call("modf", "f"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_f16 {
-  float16_t fract;
-  float16_t whole;
-};
-modf_result_f16 tint_modf(float16_t param_0) {
-  modf_result_f16 result;
-  result.fract = modf(param_0, result.whole);
-  return result;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float16_t f = float16_t(1.5h);
-  modf_result_f16 v = tint_modf(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Runtime_Modf_Vector_f32) {
-    WrapInFunction(Decl(Let("f", Call<vec3<f32>>(1.5_f, 2.5_f, 3.5_f))),  //
-                   Decl(Let("v", Call("modf", "f"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_vec3_f32 {
-  float3 fract;
-  float3 whole;
-};
-modf_result_vec3_f32 tint_modf(float3 param_0) {
-  modf_result_vec3_f32 result;
-  result.fract = modf(param_0, result.whole);
-  return result;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float3 f = float3(1.5f, 2.5f, 3.5f);
-  modf_result_vec3_f32 v = tint_modf(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Runtime_Modf_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("f", Call<vec3<f16>>(1.5_h, 2.5_h, 3.5_h))),  //
-                   Decl(Let("v", Call("modf", "f"))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_vec3_f16 {
-  vector<float16_t, 3> fract;
-  vector<float16_t, 3> whole;
-};
-modf_result_vec3_f16 tint_modf(vector<float16_t, 3> param_0) {
-  modf_result_vec3_f16 result;
-  result.fract = modf(param_0, result.whole);
-  return result;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  vector<float16_t, 3> f = vector<float16_t, 3>(float16_t(1.5h), float16_t(2.5h), float16_t(3.5h));
-  modf_result_vec3_f16 v = tint_modf(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Const_Modf_Scalar_f32) {
-    WrapInFunction(Decl(Let("v", Call("modf", 1.5_f))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_f32 {
-  float fract;
-  float whole;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  modf_result_f32 v = {0.5f, 1.0f};
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Const_Modf_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("v", Call("modf", 1.5_h))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_f16 {
-  float16_t fract;
-  float16_t whole;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  modf_result_f16 v = {float16_t(0.5h), float16_t(1.0h)};
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Const_Modf_Vector_f32) {
-    WrapInFunction(Decl(Let("v", Call("modf", Call<vec3<f32>>(1.5_f, 2.5_f, 3.5_f)))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_vec3_f32 {
-  float3 fract;
-  float3 whole;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  modf_result_vec3_f32 v = {(0.5f).xxx, float3(1.0f, 2.0f, 3.0f)};
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Const_Modf_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("v", Call("modf", Call<vec3<f16>>(1.5_h, 2.5_h, 3.5_h)))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_vec3_f16 {
-  vector<float16_t, 3> fract;
-  vector<float16_t, 3> whole;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  modf_result_vec3_f16 v = {(float16_t(0.5h)).xxx, vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h))};
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, NonInitializer_Modf_Vector_f32) {
-    WrapInFunction(
-        // Declare a variable with the result of a modf call.
-        // This is required to infer the 'var' type.
-        Decl(Var("v", Call("modf", Call<vec3<f32>>(1.5_f, 2.5_f, 3.5_f)))),
-        // Now assign 'v' again with another modf call.
-        // This requires generating a temporary variable for the struct initializer.
-        Assign("v", Call("modf", Call<vec3<f32>>(4.5_a, 5.5_a, 6.5_a))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct modf_result_vec3_f32 {
-  float3 fract;
-  float3 whole;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  modf_result_vec3_f32 v = {(0.5f).xxx, float3(1.0f, 2.0f, 3.0f)};
-  modf_result_vec3_f32 tint_symbol = {(0.5f).xxx, float3(4.0f, 5.0f, 6.0f)};
-  v = tint_symbol;
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Runtime_Frexp_Scalar_f32) {
-    WrapInFunction(Var("f", Expr(1_f)),  //
-                   Var("v", Call("frexp", "f")));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_f32 {
-  float fract;
-  int exp;
-};
-frexp_result_f32 tint_frexp(float param_0) {
-  float exp;
-  float fract = sign(param_0) * frexp(param_0, exp);
-  frexp_result_f32 result = {fract, int(exp)};
-  return result;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float f = 1.0f;
-  frexp_result_f32 v = tint_frexp(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Runtime_Frexp_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Var("f", Expr(1_h)),  //
-                   Var("v", Call("frexp", "f")));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_f16 {
-  float16_t fract;
-  int exp;
-};
-frexp_result_f16 tint_frexp(float16_t param_0) {
-  float16_t exp;
-  float16_t fract = sign(param_0) * frexp(param_0, exp);
-  frexp_result_f16 result = {fract, int(exp)};
-  return result;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float16_t f = float16_t(1.0h);
-  frexp_result_f16 v = tint_frexp(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Runtime_Frexp_Vector_f32) {
-    WrapInFunction(Var("f", Call<vec3<f32>>()),  //
-                   Var("v", Call("frexp", "f")));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_vec3_f32 {
-  float3 fract;
-  int3 exp;
-};
-frexp_result_vec3_f32 tint_frexp(float3 param_0) {
-  float3 exp;
-  float3 fract = sign(param_0) * frexp(param_0, exp);
-  frexp_result_vec3_f32 result = {fract, int3(exp)};
-  return result;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float3 f = (0.0f).xxx;
-  frexp_result_vec3_f32 v = tint_frexp(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Runtime_Frexp_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Var("f", Call<vec3<f16>>()),  //
-                   Var("v", Call("frexp", "f")));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_vec3_f16 {
-  vector<float16_t, 3> fract;
-  int3 exp;
-};
-frexp_result_vec3_f16 tint_frexp(vector<float16_t, 3> param_0) {
-  vector<float16_t, 3> exp;
-  vector<float16_t, 3> fract = sign(param_0) * frexp(param_0, exp);
-  frexp_result_vec3_f16 result = {fract, int3(exp)};
-  return result;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  vector<float16_t, 3> f = (float16_t(0.0h)).xxx;
-  frexp_result_vec3_f16 v = tint_frexp(f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Const_Frexp_Scalar_f32) {
-    WrapInFunction(Decl(Let("v", Call("frexp", 1_f))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_f32 {
-  float fract;
-  int exp;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  frexp_result_f32 v = {0.5f, 1};
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Const_Frexp_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("v", Call("frexp", 1_h))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_f16 {
-  float16_t fract;
-  int exp;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  frexp_result_f16 v = {float16_t(0.5h), 1};
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Const_Frexp_Vector_f32) {
-    WrapInFunction(Decl(Let("v", Call("frexp", Call<vec3<f32>>()))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_vec3_f32 {
-  float3 fract;
-  int3 exp;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  frexp_result_vec3_f32 v = (frexp_result_vec3_f32)0;
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Const_Frexp_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Decl(Let("v", Call("frexp", Call<vec3<f16>>()))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_vec3_f16 {
-  vector<float16_t, 3> fract;
-  int3 exp;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  frexp_result_vec3_f16 v = (frexp_result_vec3_f16)0;
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, NonInitializer_Frexp_Vector_f32) {
-    WrapInFunction(
-        // Declare a variable with the result of a frexp call.
-        // This is required to infer the 'var' type.
-        Decl(Var("v", Call("frexp", Call<vec3<f32>>(1.5_f, 2.5_f, 3.5_f)))),
-        // Now assign 'v' again with another frexp call.
-        // This requires generating a temporary variable for the struct initializer.
-        Assign("v", Call("frexp", Call<vec3<f32>>(4.5_a, 5.5_a, 6.5_a))));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct frexp_result_vec3_f32 {
-  float3 fract;
-  int3 exp;
-};
-[numthreads(1, 1, 1)]
-void test_function() {
-  frexp_result_vec3_f32 v = {float3(0.75f, 0.625f, 0.875f), int3(1, 2, 2)};
-  frexp_result_vec3_f32 tint_symbol = {float3(0.5625f, 0.6875f, 0.8125f), (3).xxx};
-  v = tint_symbol;
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Degrees_Scalar_f32) {
-    auto* val = Var("val", ty.f32());
-    auto* call = Call("degrees", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float tint_degrees(float param_0) {
-  return param_0 * 57.29577951308232286465;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float val = 0.0f;
-  float tint_symbol = tint_degrees(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Degrees_Vector_f32) {
-    auto* val = Var("val", ty.vec3<f32>());
-    auto* call = Call("degrees", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float3 tint_degrees(float3 param_0) {
-  return param_0 * 57.29577951308232286465;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float3 val = float3(0.0f, 0.0f, 0.0f);
-  float3 tint_symbol = tint_degrees(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Degrees_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.f16());
-    auto* call = Call("degrees", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float16_t tint_degrees(float16_t param_0) {
-  return param_0 * 57.29577951308232286465;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float16_t val = float16_t(0.0h);
-  float16_t tint_symbol = tint_degrees(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Degrees_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.vec3<f16>());
-    auto* call = Call("degrees", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(vector<float16_t, 3> tint_degrees(vector<float16_t, 3> param_0) {
-  return param_0 * 57.29577951308232286465;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  vector<float16_t, 3> val = vector<float16_t, 3>(float16_t(0.0h), float16_t(0.0h), float16_t(0.0h));
-  vector<float16_t, 3> tint_symbol = tint_degrees(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Radians_Scalar_f32) {
-    auto* val = Var("val", ty.f32());
-    auto* call = Call("radians", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float tint_radians(float param_0) {
-  return param_0 * 0.01745329251994329547;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float val = 0.0f;
-  float tint_symbol = tint_radians(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Radians_Vector_f32) {
-    auto* val = Var("val", ty.vec3<f32>());
-    auto* call = Call("radians", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float3 tint_radians(float3 param_0) {
-  return param_0 * 0.01745329251994329547;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float3 val = float3(0.0f, 0.0f, 0.0f);
-  float3 tint_symbol = tint_radians(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Radians_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.f16());
-    auto* call = Call("radians", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float16_t tint_radians(float16_t param_0) {
-  return param_0 * 0.01745329251994329547;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float16_t val = float16_t(0.0h);
-  float16_t tint_symbol = tint_radians(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Radians_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.vec3<f16>());
-    auto* call = Call("radians", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(vector<float16_t, 3> tint_radians(vector<float16_t, 3> param_0) {
-  return param_0 * 0.01745329251994329547;
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  vector<float16_t, 3> val = vector<float16_t, 3>(float16_t(0.0h), float16_t(0.0h), float16_t(0.0h));
-  vector<float16_t, 3> tint_symbol = tint_radians(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Sign_Scalar_i32) {
-    auto* val = Var("val", ty.i32());
-    auto* call = Call("sign", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void test_function() {
-  int val = 0;
-  int tint_symbol = int(sign(val));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Sign_Vector_i32) {
-    auto* val = Var("val", ty.vec3<i32>());
-    auto* call = Call("sign", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void test_function() {
-  int3 val = int3(0, 0, 0);
-  int3 tint_symbol = int3(sign(val));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Sign_Scalar_f32) {
-    auto* val = Var("val", ty.f32());
-    auto* call = Call("sign", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void test_function() {
-  float val = 0.0f;
-  float tint_symbol = float(sign(val));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Sign_Vector_f32) {
-    auto* val = Var("val", ty.vec3<f32>());
-    auto* call = Call("sign", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void test_function() {
-  float3 val = float3(0.0f, 0.0f, 0.0f);
-  float3 tint_symbol = float3(sign(val));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Sign_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.f16());
-    auto* call = Call("sign", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void test_function() {
-  float16_t val = float16_t(0.0h);
-  float16_t tint_symbol = float16_t(sign(val));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Sign_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.vec3<f16>());
-    auto* call = Call("sign", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void test_function() {
-  vector<float16_t, 3> val = vector<float16_t, 3>(float16_t(0.0h), float16_t(0.0h), float16_t(0.0h));
-  vector<float16_t, 3> tint_symbol = vector<float16_t, 3>(sign(val));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Trunc_Scalar_f32) {
-    auto* val = Var("val", ty.f32());
-    auto* call = Call("trunc", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float tint_trunc(float param_0) {
-  return param_0 < 0 ? ceil(param_0) : floor(param_0);
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float val = 0.0f;
-  float tint_symbol = tint_trunc(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Trunc_Vector_f32) {
-    auto* val = Var("val", ty.vec3<f32>());
-    auto* call = Call("trunc", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float3 tint_trunc(float3 param_0) {
-  return param_0 < 0 ? ceil(param_0) : floor(param_0);
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float3 val = float3(0.0f, 0.0f, 0.0f);
-  float3 tint_symbol = tint_trunc(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Trunc_Scalar_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.f16());
-    auto* call = Call("trunc", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float16_t tint_trunc(float16_t param_0) {
-  return param_0 < 0 ? ceil(param_0) : floor(param_0);
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float16_t val = float16_t(0.0h);
-  float16_t tint_symbol = tint_trunc(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Trunc_Vector_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* val = Var("val", ty.vec3<f16>());
-    auto* call = Call("trunc", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(vector<float16_t, 3> tint_trunc(vector<float16_t, 3> param_0) {
-  return param_0 < 0 ? ceil(param_0) : floor(param_0);
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  vector<float16_t, 3> val = vector<float16_t, 3>(float16_t(0.0h), float16_t(0.0h), float16_t(0.0h));
-  vector<float16_t, 3> tint_symbol = tint_trunc(val);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Pack4x8Snorm) {
-    auto* call = Call("pack4x8snorm", "p1");
-    GlobalVar("p1", ty.vec4<f32>(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(uint tint_pack4x8snorm(float4 param_0) {
-  int4 i = int4(round(clamp(param_0, -1.0, 1.0) * 127.0)) & 0xff;
-  return asuint(i.x | i.y << 8 | i.z << 16 | i.w << 24);
-}
-
-static float4 p1 = float4(0.0f, 0.0f, 0.0f, 0.0f);
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  uint r = tint_pack4x8snorm(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Pack4x8Unorm) {
-    auto* call = Call("pack4x8unorm", "p1");
-    GlobalVar("p1", ty.vec4<f32>(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(uint tint_pack4x8unorm(float4 param_0) {
-  uint4 i = uint4(round(clamp(param_0, 0.0, 1.0) * 255.0));
-  return (i.x | i.y << 8 | i.z << 16 | i.w << 24);
-}
-
-static float4 p1 = float4(0.0f, 0.0f, 0.0f, 0.0f);
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  uint r = tint_pack4x8unorm(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Pack2x16Snorm) {
-    auto* call = Call("pack2x16snorm", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(uint tint_pack2x16snorm(float2 param_0) {
-  int2 i = int2(round(clamp(param_0, -1.0, 1.0) * 32767.0)) & 0xffff;
-  return asuint(i.x | i.y << 16);
-}
-
-static float2 p1 = float2(0.0f, 0.0f);
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  uint r = tint_pack2x16snorm(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Pack2x16Unorm) {
-    auto* call = Call("pack2x16unorm", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(uint tint_pack2x16unorm(float2 param_0) {
-  uint2 i = uint2(round(clamp(param_0, 0.0, 1.0) * 65535.0));
-  return (i.x | i.y << 16);
-}
-
-static float2 p1 = float2(0.0f, 0.0f);
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  uint r = tint_pack2x16unorm(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Pack2x16Float) {
-    auto* call = Call("pack2x16float", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(uint tint_pack2x16float(float2 param_0) {
-  uint2 i = f32tof16(param_0);
-  return i.x | (i.y << 16);
-}
-
-static float2 p1 = float2(0.0f, 0.0f);
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  uint r = tint_pack2x16float(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Unpack4x8Snorm) {
-    auto* call = Call("unpack4x8snorm", "p1");
-    GlobalVar("p1", ty.u32(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float4 tint_unpack4x8snorm(uint param_0) {
-  int j = int(param_0);
-  int4 i = int4(j << 24, j << 16, j << 8, j) >> 24;
-  return clamp(float4(i) / 127.0, -1.0, 1.0);
-}
-
-static uint p1 = 0u;
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float4 r = tint_unpack4x8snorm(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Unpack4x8Unorm) {
-    auto* call = Call("unpack4x8unorm", "p1");
-    GlobalVar("p1", ty.u32(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float4 tint_unpack4x8unorm(uint param_0) {
-  uint j = param_0;
-  uint4 i = uint4(j & 0xff, (j >> 8) & 0xff, (j >> 16) & 0xff, j >> 24);
-  return float4(i) / 255.0;
-}
-
-static uint p1 = 0u;
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float4 r = tint_unpack4x8unorm(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Unpack2x16Snorm) {
-    auto* call = Call("unpack2x16snorm", "p1");
-    GlobalVar("p1", ty.u32(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float2 tint_unpack2x16snorm(uint param_0) {
-  int j = int(param_0);
-  int2 i = int2(j << 16, j) >> 16;
-  return clamp(float2(i) / 32767.0, -1.0, 1.0);
-}
-
-static uint p1 = 0u;
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float2 r = tint_unpack2x16snorm(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Unpack2x16Unorm) {
-    auto* call = Call("unpack2x16unorm", "p1");
-    GlobalVar("p1", ty.u32(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float2 tint_unpack2x16unorm(uint param_0) {
-  uint j = param_0;
-  uint2 i = uint2(j & 0xffff, j >> 16);
-  return float2(i) / 65535.0;
-}
-
-static uint p1 = 0u;
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float2 r = tint_unpack2x16unorm(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Unpack2x16Float) {
-    auto* call = Call("unpack2x16float", "p1");
-    GlobalVar("p1", ty.u32(), core::AddressSpace::kPrivate);
-    WrapInFunction(Decl(Var("r", call)));
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float2 tint_unpack2x16float(uint param_0) {
-  uint i = param_0;
-  return f16tof32(uint2(i & 0xffff, i >> 16));
-}
-
-static uint p1 = 0u;
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float2 r = tint_unpack2x16float(p1);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, StorageBarrier) {
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             CallStmt(Call("storageBarrier")),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void main() {
-  DeviceMemoryBarrierWithGroupSync();
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, WorkgroupBarrier) {
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             CallStmt(Call("workgroupBarrier")),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void main() {
-  GroupMemoryBarrierWithGroupSync();
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Dot4I8Packed) {
-    Require(wgsl::LanguageFeature::kPacked4X8IntegerDotProduct);
-
-    auto* val1 = Var("val1", ty.u32());
-    auto* val2 = Var("val2", ty.u32());
-    auto* call = Call("dot4I8Packed", val1, val2);
-    WrapInFunction(val1, val2, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(int tint_dot4I8Packed(uint param_0, uint param_1) {
-  int accumulator = 0;
-  return dot4add_i8packed(param_0, param_1, accumulator);
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  uint val1 = 0u;
-  uint val2 = 0u;
-  int tint_symbol = tint_dot4I8Packed(val1, val2);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, Dot4U8Packed) {
-    Require(wgsl::LanguageFeature::kPacked4X8IntegerDotProduct);
-
-    auto* val1 = Var("val1", ty.u32());
-    auto* val2 = Var("val2", ty.u32());
-    auto* call = Call("dot4U8Packed", val1, val2);
-    WrapInFunction(val1, val2, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(uint tint_dot4U8Packed(uint param_0, uint param_1) {
-  uint accumulator = 0u;
-  return dot4add_u8packed(param_0, param_1, accumulator);
-}
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  uint val1 = 0u;
-  uint val2 = 0u;
-  uint tint_symbol = tint_dot4U8Packed(val1, val2);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, CountOneBits) {
-    auto* val = Var("val1", ty.i32());
-    auto* call = Call("countOneBits", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void test_function() {
-  int val1 = 0;
-  int tint_symbol = asint(countbits(asuint(val1)));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Builtin, ReverseBits) {
-    auto* val = Var("val1", ty.i32());
-    auto* call = Call("reverseBits", val);
-    WrapInFunction(val, call);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void test_function() {
-  int val1 = 0;
-  int tint_symbol = asint(reversebits(asuint(val1)));
-  return;
-}
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/builtin_texture_test.cc b/src/tint/lang/hlsl/writer/ast_printer/builtin_texture_test.cc
deleted file mode 100644
index ef0918f..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/builtin_texture_test.cc
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/builtin_texture_helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using ::testing::HasSubstr;
-
-struct ExpectedResult {
-    ExpectedResult(const char* o) : out(o) {}  // NOLINT
-    ExpectedResult(const char* p, const char* o) : pre(p), out(o) {}
-
-    std::string pre;
-    std::string out;
-};
-
-ExpectedResult expected_texture_overload(ast::test::ValidTextureOverload overload) {
-    using ValidTextureOverload = ast::test::ValidTextureOverload;
-    switch (overload) {
-        case ValidTextureOverload::kDimensions1d:
-        case ValidTextureOverload::kDimensionsStorageWO1d:
-            return {
-                R"(int tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp);
-)",
-                "tint_tmp;",
-            };
-        case ValidTextureOverload::kDimensions2d:
-        case ValidTextureOverload::kDimensionsDepth2d:
-        case ValidTextureOverload::kDimensionsStorageWO2d:
-            return {
-                R"(int2 tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp.x, tint_tmp.y);
-)",
-                "tint_tmp;",
-            };
-        case ValidTextureOverload::kDimensionsDepthMultisampled2d:
-        case ValidTextureOverload::kDimensionsMultisampled2d:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp.xy;",
-            };
-
-        case ValidTextureOverload::kDimensions2dArray:
-        case ValidTextureOverload::kDimensionsDepth2dArray:
-        case ValidTextureOverload::kDimensionsStorageWO2dArray:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp.xy;",
-            };
-        case ValidTextureOverload::kDimensions3d:
-        case ValidTextureOverload::kDimensionsStorageWO3d:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp;",
-            };
-        case ValidTextureOverload::kDimensionsCube:
-        case ValidTextureOverload::kDimensionsDepthCube:
-            return {
-                R"(int2 tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp.x, tint_tmp.y);
-)",
-                "tint_tmp;",
-            };
-        case ValidTextureOverload::kDimensionsCubeArray:
-        case ValidTextureOverload::kDimensionsDepthCubeArray:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp.xy;",
-            };
-        case ValidTextureOverload::kDimensions2dLevel:
-        case ValidTextureOverload::kDimensionsDepth2dLevel:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(1, tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp.xy;",
-            };
-        case ValidTextureOverload::kDimensions2dArrayLevel:
-        case ValidTextureOverload::kDimensionsDepth2dArrayLevel:
-            return {
-                R"(int4 tint_tmp;
-  tint_symbol.GetDimensions(1, tint_tmp.x, tint_tmp.y, tint_tmp.z, tint_tmp.w);
-)",
-                "tint_tmp.xy;",
-            };
-        case ValidTextureOverload::kDimensions3dLevel:
-            return {
-                R"(int4 tint_tmp;
-  tint_symbol.GetDimensions(1, tint_tmp.x, tint_tmp.y, tint_tmp.z, tint_tmp.w);
-)",
-                "tint_tmp.xyz;",
-            };
-        case ValidTextureOverload::kDimensionsCubeLevel:
-        case ValidTextureOverload::kDimensionsDepthCubeLevel:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(1, tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp.xy;",
-            };
-        case ValidTextureOverload::kDimensionsCubeArrayLevel:
-        case ValidTextureOverload::kDimensionsDepthCubeArrayLevel:
-            return {
-                R"(int4 tint_tmp;
-  tint_symbol.GetDimensions(1, tint_tmp.x, tint_tmp.y, tint_tmp.z, tint_tmp.w);
-)",
-                "tint_tmp.xy;",
-            };
-        case ValidTextureOverload::kGather2dF32:
-            return R"(tint_symbol.GatherRed(tint_symbol_1, float2(1.0f, 2.0f)))";
-        case ValidTextureOverload::kGather2dOffsetF32:
-            return R"(tint_symbol.GatherRed(tint_symbol_1, float2(1.0f, 2.0f), int2(3, 4)))";
-        case ValidTextureOverload::kGather2dArrayF32:
-            return R"(tint_symbol.GatherRed(tint_symbol_1, float3(1.0f, 2.0f, float(3))))";
-        case ValidTextureOverload::kGather2dArrayOffsetF32:
-            return R"(tint_symbol.GatherRed(tint_symbol_1, float3(1.0f, 2.0f, float(3u)), int2(4, 5)))";
-        case ValidTextureOverload::kGatherCubeF32:
-            return R"(tint_symbol.GatherRed(tint_symbol_1, float3(1.0f, 2.0f, 3.0f)))";
-        case ValidTextureOverload::kGatherCubeArrayF32:
-            return R"(tint_symbol.GatherRed(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4u))))";
-        case ValidTextureOverload::kGatherDepth2dF32:
-            return R"(tint_symbol.Gather(tint_symbol_1, float2(1.0f, 2.0f)))";
-        case ValidTextureOverload::kGatherDepth2dOffsetF32:
-            return R"(tint_symbol.Gather(tint_symbol_1, float2(1.0f, 2.0f), int2(3, 4)))";
-        case ValidTextureOverload::kGatherDepth2dArrayF32:
-            return R"(tint_symbol.Gather(tint_symbol_1, float3(1.0f, 2.0f, float(3u))))";
-        case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
-            return R"(tint_symbol.Gather(tint_symbol_1, float3(1.0f, 2.0f, float(3)), int2(4, 5)))";
-        case ValidTextureOverload::kGatherDepthCubeF32:
-            return R"(tint_symbol.Gather(tint_symbol_1, float3(1.0f, 2.0f, 3.0f)))";
-        case ValidTextureOverload::kGatherDepthCubeArrayF32:
-            return R"(tint_symbol.Gather(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4u))))";
-        case ValidTextureOverload::kGatherCompareDepth2dF32:
-            return R"(tint_symbol.GatherCmp(tint_symbol_1, float2(1.0f, 2.0f), 3.0f))";
-        case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
-            return R"(tint_symbol.GatherCmp(tint_symbol_1, float2(1.0f, 2.0f), 3.0f, int2(4, 5)))";
-        case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
-            return R"(tint_symbol.GatherCmp(tint_symbol_1, float3(1.0f, 2.0f, float(3)), 4.0f))";
-        case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
-            return R"(tint_symbol.GatherCmp(tint_symbol_1, float3(1.0f, 2.0f, float(3)), 4.0f, int2(5, 6)))";
-        case ValidTextureOverload::kGatherCompareDepthCubeF32:
-            return R"(tint_symbol.GatherCmp(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), 4.0f))";
-        case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
-            return R"(tint_symbol.GatherCmp(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4u)), 5.0f))";
-        case ValidTextureOverload::kNumLayers2dArray:
-        case ValidTextureOverload::kNumLayersDepth2dArray:
-        case ValidTextureOverload::kNumLayersCubeArray:
-        case ValidTextureOverload::kNumLayersDepthCubeArray:
-        case ValidTextureOverload::kNumLayersStorageWO2dArray:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp.z;",
-            };
-        case ValidTextureOverload::kNumLevels2d:
-        case ValidTextureOverload::kNumLevelsCube:
-        case ValidTextureOverload::kNumLevelsDepth2d:
-        case ValidTextureOverload::kNumLevelsDepthCube:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(0, tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp.z;",
-            };
-        case ValidTextureOverload::kNumLevels2dArray:
-        case ValidTextureOverload::kNumLevels3d:
-        case ValidTextureOverload::kNumLevelsCubeArray:
-        case ValidTextureOverload::kNumLevelsDepth2dArray:
-        case ValidTextureOverload::kNumLevelsDepthCubeArray:
-            return {
-                R"(int4 tint_tmp;
-  tint_symbol.GetDimensions(0, tint_tmp.x, tint_tmp.y, tint_tmp.z, tint_tmp.w);
-)",
-                "tint_tmp.w;",
-            };
-        case ValidTextureOverload::kNumSamplesDepthMultisampled2d:
-        case ValidTextureOverload::kNumSamplesMultisampled2d:
-            return {
-                R"(int3 tint_tmp;
-  tint_symbol.GetDimensions(tint_tmp.x, tint_tmp.y, tint_tmp.z);
-)",
-                "tint_tmp.z;",
-            };
-        case ValidTextureOverload::kSample1dF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, 1.0f);)";
-        case ValidTextureOverload::kSample2dF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float2(1.0f, 2.0f));)";
-        case ValidTextureOverload::kSample2dOffsetF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float2(1.0f, 2.0f), int2(3, 4));)";
-        case ValidTextureOverload::kSample2dArrayF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float3(1.0f, 2.0f, float(3)));)";
-        case ValidTextureOverload::kSample2dArrayOffsetF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float3(1.0f, 2.0f, float(3u)), int2(4, 5));)";
-        case ValidTextureOverload::kSample3dF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float3(1.0f, 2.0f, 3.0f));)";
-        case ValidTextureOverload::kSample3dOffsetF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), int3(4, 5, 6));)";
-        case ValidTextureOverload::kSampleCubeF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float3(1.0f, 2.0f, 3.0f));)";
-        case ValidTextureOverload::kSampleCubeArrayF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4)));)";
-        case ValidTextureOverload::kSampleDepth2dF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float2(1.0f, 2.0f)).x;)";
-        case ValidTextureOverload::kSampleDepth2dOffsetF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float2(1.0f, 2.0f), int2(3, 4)).x;)";
-        case ValidTextureOverload::kSampleDepth2dArrayF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float3(1.0f, 2.0f, float(3))).x;)";
-        case ValidTextureOverload::kSampleDepth2dArrayOffsetF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float3(1.0f, 2.0f, float(3)), int2(4, 5)).x;)";
-        case ValidTextureOverload::kSampleDepthCubeF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float3(1.0f, 2.0f, 3.0f)).x;)";
-        case ValidTextureOverload::kSampleDepthCubeArrayF32:
-            return R"(tint_symbol.Sample(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4u))).x;)";
-        case ValidTextureOverload::kSampleBias2dF32:
-            return R"(tint_symbol.SampleBias(tint_symbol_1, float2(1.0f, 2.0f), clamp(3.0f, -16.0f, 15.99f));)";
-        case ValidTextureOverload::kSampleBias2dOffsetF32:
-            return R"(tint_symbol.SampleBias(tint_symbol_1, float2(1.0f, 2.0f), clamp(3.0f, -16.0f, 15.99f), int2(4, 5));)";
-        case ValidTextureOverload::kSampleBias2dArrayF32:
-            return R"(tint_symbol.SampleBias(tint_symbol_1, float3(1.0f, 2.0f, float(4u)), clamp(3.0f, -16.0f, 15.99f));)";
-        case ValidTextureOverload::kSampleBias2dArrayOffsetF32:
-            return R"(tint_symbol.SampleBias(tint_symbol_1, float3(1.0f, 2.0f, float(3)), clamp(4.0f, -16.0f, 15.99f), int2(5, 6));)";
-        case ValidTextureOverload::kSampleBias3dF32:
-            return R"(tint_symbol.SampleBias(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), clamp(4.0f, -16.0f, 15.99f));)";
-        case ValidTextureOverload::kSampleBias3dOffsetF32:
-            return R"(tint_symbol.SampleBias(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), clamp(4.0f, -16.0f, 15.99f), int3(5, 6, 7));)";
-        case ValidTextureOverload::kSampleBiasCubeF32:
-            return R"(tint_symbol.SampleBias(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), clamp(4.0f, -16.0f, 15.99f));)";
-        case ValidTextureOverload::kSampleBiasCubeArrayF32:
-            return R"(tint_symbol.SampleBias(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(3)), clamp(4.0f, -16.0f, 15.99f));)";
-        case ValidTextureOverload::kSampleLevel2dF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float2(1.0f, 2.0f), 3.0f);)";
-        case ValidTextureOverload::kSampleLevel2dOffsetF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float2(1.0f, 2.0f), 3.0f, int2(4, 5));)";
-        case ValidTextureOverload::kSampleLevel2dArrayF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float3(1.0f, 2.0f, float(3)), 4.0f);)";
-        case ValidTextureOverload::kSampleLevel2dArrayOffsetF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float3(1.0f, 2.0f, float(3)), 4.0f, int2(5, 6));)";
-        case ValidTextureOverload::kSampleLevel3dF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), 4.0f);)";
-        case ValidTextureOverload::kSampleLevel3dOffsetF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), 4.0f, int3(5, 6, 7));)";
-        case ValidTextureOverload::kSampleLevelCubeF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), 4.0f);)";
-        case ValidTextureOverload::kSampleLevelCubeArrayF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4)), 5.0f);)";
-        case ValidTextureOverload::kSampleLevelDepth2dF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float2(1.0f, 2.0f), 3u).x;)";
-        case ValidTextureOverload::kSampleLevelDepth2dOffsetF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float2(1.0f, 2.0f), 3, int2(4, 5)).x;)";
-        case ValidTextureOverload::kSampleLevelDepth2dArrayF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float3(1.0f, 2.0f, float(3u)), 4u).x;)";
-        case ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float3(1.0f, 2.0f, float(3u)), 4u, int2(5, 6)).x;)";
-        case ValidTextureOverload::kSampleLevelDepthCubeF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), 4).x;)";
-        case ValidTextureOverload::kSampleLevelDepthCubeArrayF32:
-            return R"(tint_symbol.SampleLevel(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4)), 5).x;)";
-        case ValidTextureOverload::kSampleGrad2dF32:
-            return R"(tint_symbol.SampleGrad(tint_symbol_1, float2(1.0f, 2.0f), float2(3.0f, 4.0f), float2(5.0f, 6.0f));)";
-        case ValidTextureOverload::kSampleGrad2dOffsetF32:
-            return R"(tint_symbol.SampleGrad(tint_symbol_1, float2(1.0f, 2.0f), float2(3.0f, 4.0f), float2(5.0f, 6.0f), int2((7).xx));)";
-        case ValidTextureOverload::kSampleGrad2dArrayF32:
-            return R"(tint_symbol.SampleGrad(tint_symbol_1, float3(1.0f, 2.0f, float(3)), float2(4.0f, 5.0f), float2(6.0f, 7.0f));)";
-        case ValidTextureOverload::kSampleGrad2dArrayOffsetF32:
-            return R"(tint_symbol.SampleGrad(tint_symbol_1, float3(1.0f, 2.0f, float(3u)), float2(4.0f, 5.0f), float2(6.0f, 7.0f), int2(6, 7));)";
-        case ValidTextureOverload::kSampleGrad3dF32:
-            return R"(tint_symbol.SampleGrad(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));)";
-        case ValidTextureOverload::kSampleGrad3dOffsetF32:
-            return R"(tint_symbol.SampleGrad(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f), int3(0, 1, 2));)";
-        case ValidTextureOverload::kSampleGradCubeF32:
-            return R"(tint_symbol.SampleGrad(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));)";
-        case ValidTextureOverload::kSampleGradCubeArrayF32:
-            return R"(tint_symbol.SampleGrad(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4u)), float3(5.0f, 6.0f, 7.0f), float3(8.0f, 9.0f, 10.0f));)";
-        case ValidTextureOverload::kSampleCompareDepth2dF32:
-            return R"(tint_symbol.SampleCmp(tint_symbol_1, float2(1.0f, 2.0f), 3.0f);)";
-        case ValidTextureOverload::kSampleCompareDepth2dOffsetF32:
-            return R"(tint_symbol.SampleCmp(tint_symbol_1, float2(1.0f, 2.0f), 3.0f, int2(4, 5));)";
-        case ValidTextureOverload::kSampleCompareDepth2dArrayF32:
-            return R"(tint_symbol.SampleCmp(tint_symbol_1, float3(1.0f, 2.0f, float(4)), 3.0f);)";
-        case ValidTextureOverload::kSampleCompareDepth2dArrayOffsetF32:
-            return R"(tint_symbol.SampleCmp(tint_symbol_1, float3(1.0f, 2.0f, float(4u)), 3.0f, int2(5, 6));)";
-        case ValidTextureOverload::kSampleCompareDepthCubeF32:
-            return R"(tint_symbol.SampleCmp(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), 4.0f);)";
-        case ValidTextureOverload::kSampleCompareDepthCubeArrayF32:
-            return R"(tint_symbol.SampleCmp(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4)), 5.0f);)";
-        case ValidTextureOverload::kSampleCompareLevelDepth2dF32:
-            return R"(tint_symbol.SampleCmpLevelZero(tint_symbol_1, float2(1.0f, 2.0f), 3.0f);)";
-        case ValidTextureOverload::kSampleCompareLevelDepth2dOffsetF32:
-            return R"(tint_symbol.SampleCmpLevelZero(tint_symbol_1, float2(1.0f, 2.0f), 3.0f, int2(4, 5));)";
-        case ValidTextureOverload::kSampleCompareLevelDepth2dArrayF32:
-            return R"(tint_symbol.SampleCmpLevelZero(tint_symbol_1, float3(1.0f, 2.0f, float(3)), 4.0f);)";
-        case ValidTextureOverload::kSampleCompareLevelDepth2dArrayOffsetF32:
-            return R"(tint_symbol.SampleCmpLevelZero(tint_symbol_1, float3(1.0f, 2.0f, float(3)), 4.0f, int2(5, 6));)";
-        case ValidTextureOverload::kSampleCompareLevelDepthCubeF32:
-            return R"(tint_symbol.SampleCmpLevelZero(tint_symbol_1, float3(1.0f, 2.0f, 3.0f), 4.0f);)";
-        case ValidTextureOverload::kSampleCompareLevelDepthCubeArrayF32:
-            return R"(tint_symbol.SampleCmpLevelZero(tint_symbol_1, float4(1.0f, 2.0f, 3.0f, float(4)), 5.0f);)";
-        case ValidTextureOverload::kLoad1dLevelF32:
-            return R"(tint_symbol.Load(uint2(1u, 3u));)";
-        case ValidTextureOverload::kLoad1dLevelU32:
-        case ValidTextureOverload::kLoad1dLevelI32:
-            return R"(tint_symbol.Load(int2(1, 3));)";
-        case ValidTextureOverload::kLoad2dLevelU32:
-            return R"(tint_symbol.Load(int3(1, 2, 3));)";
-        case ValidTextureOverload::kLoad2dLevelF32:
-        case ValidTextureOverload::kLoad2dLevelI32:
-            return R"(tint_symbol.Load(uint3(1u, 2u, 3u));)";
-        case ValidTextureOverload::kLoad2dArrayLevelF32:
-        case ValidTextureOverload::kLoad2dArrayLevelU32:
-        case ValidTextureOverload::kLoad3dLevelF32:
-        case ValidTextureOverload::kLoad3dLevelU32:
-            return R"(tint_symbol.Load(int4(1, 2, 3, 4));)";
-        case ValidTextureOverload::kLoad2dArrayLevelI32:
-        case ValidTextureOverload::kLoad3dLevelI32:
-            return R"(tint_symbol.Load(uint4(1u, 2u, 3u, 4u));)";
-        case ValidTextureOverload::kLoadMultisampled2dF32:
-        case ValidTextureOverload::kLoadMultisampled2dU32:
-            return R"(tint_symbol.Load(int2(1, 2), 3);)";
-        case ValidTextureOverload::kLoadMultisampled2dI32:
-            return R"(tint_symbol.Load(uint2(1u, 2u), 3u);)";
-        case ValidTextureOverload::kLoadDepth2dLevelF32:
-            return R"(tint_symbol.Load(int3(1, 2, 3)).x;)";
-        case ValidTextureOverload::kLoadDepth2dArrayLevelF32:
-            return R"(tint_symbol.Load(uint4(1u, 2u, 3u, 4u)).x;)";
-        case ValidTextureOverload::kLoadDepthMultisampled2dF32:
-            return R"(tint_symbol.Load(uint3(1u, 2u, uint(0)), 3u).x;)";
-        case ValidTextureOverload::kStoreWO1dRgba32float:
-            return R"(tint_symbol[1] = float4(2.0f, 3.0f, 4.0f, 5.0f);)";
-        case ValidTextureOverload::kStoreWO2dRgba32float:
-            return R"(tint_symbol[int2(1, 2)] = float4(3.0f, 4.0f, 5.0f, 6.0f);)";
-        case ValidTextureOverload::kStoreWO2dArrayRgba32float:
-            return R"(tint_symbol[uint3(1u, 2u, 3u)] = float4(4.0f, 5.0f, 6.0f, 7.0f);)";
-        case ValidTextureOverload::kStoreWO3dRgba32float:
-            return R"(tint_symbol[uint3(1u, 2u, 3u)] = float4(4.0f, 5.0f, 6.0f, 7.0f);)";
-    }
-    return "<unmatched texture overload>";
-}  // NOLINT - Ignore the length of this function
-
-class HlslGeneratorBuiltinTextureTest : public TestParamHelper<ast::test::TextureOverloadCase> {};
-
-TEST_P(HlslGeneratorBuiltinTextureTest, Call) {
-    auto param = GetParam();
-
-    param.BuildTextureVariable(this);
-    param.BuildSamplerVariable(this);
-
-    auto* call = Call(param.function, param.args(this));
-    auto* stmt = param.returns_value ? static_cast<const ast::Statement*>(Decl(Var("v", call)))
-                                     : static_cast<const ast::Statement*>(CallStmt(call));
-
-    Func("main", tint::Empty, ty.void_(), Vector{stmt},
-         Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto expected = expected_texture_overload(param.overload);
-
-    EXPECT_THAT(gen.Result(), HasSubstr(expected.pre));
-    EXPECT_THAT(gen.Result(), HasSubstr(expected.out));
-}
-
-INSTANTIATE_TEST_SUITE_P(HlslGeneratorBuiltinTextureTest,
-                         HlslGeneratorBuiltinTextureTest,
-                         testing::ValuesIn(ast::test::TextureOverloadCase::ValidCases()));
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/call_test.cc b/src/tint/lang/hlsl/writer/ast_printer/call_test.cc
deleted file mode 100644
index fbffc01..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/call_test.cc
+++ /dev/null
@@ -1,93 +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/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Call = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Call, EmitExpression_Call_WithoutParams) {
-    Func("my_func", tint::Empty, ty.f32(), Vector{Return(1.23_f)});
-
-    auto* call = Call("my_func");
-    WrapInFunction(call);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "my_func()");
-}
-
-TEST_F(HlslASTPrinterTest_Call, EmitExpression_Call_WithParams) {
-    Func("my_func",
-         Vector{
-             Param(Sym(), ty.f32()),
-             Param(Sym(), ty.f32()),
-         },
-         ty.f32(), Vector{Return(1.23_f)});
-    GlobalVar("param1", ty.f32(), core::AddressSpace::kPrivate);
-    GlobalVar("param2", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* call = Call("my_func", "param1", "param2");
-    WrapInFunction(call);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "my_func(param1, param2)");
-}
-
-TEST_F(HlslASTPrinterTest_Call, EmitStatement_Call) {
-    Func("my_func",
-         Vector{
-             Param(Sym(), ty.f32()),
-             Param(Sym(), ty.f32()),
-         },
-         ty.void_(), tint::Empty, tint::Empty);
-    GlobalVar("param1", ty.f32(), core::AddressSpace::kPrivate);
-    GlobalVar("param2", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* call = CallStmt(Call("my_func", "param1", "param2"));
-    WrapInFunction(call);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-    ASSERT_TRUE(gen.EmitStatement(call)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  my_func(param1, param2);\n");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/case_test.cc b/src/tint/lang/hlsl/writer/ast_printer/case_test.cc
deleted file mode 100644
index 82bafe9..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/case_test.cc
+++ /dev/null
@@ -1,103 +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/hlsl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Case = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Case, Emit_Case) {
-    auto* s =
-        Switch(1_i, Case(CaseSelector(5_i), Block(create<ast::BreakStatement>())), DefaultCase());
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  case 5: {
-    break;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Case, Emit_Case_BreaksByDefault) {
-    auto* s = Switch(1_i, Case(CaseSelector(5_i), Block()), DefaultCase());
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  case 5: {
-    break;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Case, Emit_Case_MultipleSelectors) {
-    auto* s = Switch(
-        1_i,
-        Case(Vector{CaseSelector(5_i), CaseSelector(6_i)}, Block(create<ast::BreakStatement>())),
-        DefaultCase());
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitCase(s, 0)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  case 5:
-  case 6: {
-    break;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Case, Emit_Case_Default) {
-    auto* s = Switch(1_i, DefaultCase(Block(create<ast::BreakStatement>())));
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitCase(s, 0u)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  default: {
-    break;
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/cast_test.cc b/src/tint/lang/hlsl/writer/ast_printer/cast_test.cc
deleted file mode 100644
index 739a364..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/cast_test.cc
+++ /dev/null
@@ -1,62 +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/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using HlslASTPrinterTest_Cast = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Cast, EmitExpression_Cast_Scalar) {
-    auto* cast = Call<f32>(1_i);
-    WrapInFunction(cast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "1.0f");
-}
-
-TEST_F(HlslASTPrinterTest_Cast, EmitExpression_Cast_Vector) {
-    auto* cast = Call<vec3<f32>>(Call<vec3<i32>>(1_i, 2_i, 3_i));
-    WrapInFunction(cast);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float3(1.0f, 2.0f, 3.0f)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/const_assert_test.cc b/src/tint/lang/hlsl/writer/ast_printer/const_assert_test.cc
deleted file mode 100644
index 3b4c728..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/const_assert_test.cc
+++ /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.
-
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest = TestHelper;
-
-TEST_F(HlslASTPrinterTest, Emit_GlobalConstAssert) {
-    GlobalConstAssert(true);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    // const asserts are not emitted
-    EXPECT_EQ(gen.Result(), "");
-}
-
-TEST_F(HlslASTPrinterTest, Emit_FunctionConstAssert) {
-    Func("f", tint::Empty, ty.void_(), Vector{ConstAssert(true)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    // const asserts are not emitted
-    EXPECT_EQ(gen.Result(), R"(void f() {
-}
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/constructor_test.cc b/src/tint/lang/hlsl/writer/ast_printer/constructor_test.cc
deleted file mode 100644
index 5a9a9a3..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/constructor_test.cc
+++ /dev/null
@@ -1,465 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using ::testing::HasSubstr;
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using HlslASTPrinterTest_Constructor = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Constructor, Bool) {
-    WrapInFunction(Expr(false));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("false"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Int) {
-    WrapInFunction(Expr(-12345_i));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("-12345"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, UInt) {
-    WrapInFunction(Expr(56779_u));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("56779u"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Float) {
-    // Use a number close to 1<<30 but whose decimal representation ends in 0.
-    WrapInFunction(Expr(f32((1 << 30) - 4)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("1073741824.0f"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, F16) {
-    Enable(wgsl::Extension::kF16);
-
-    // Use a number close to 1<<16 but whose decimal representation ends in 0.
-    WrapInFunction(Expr(f16((1 << 15) - 8)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("float16_t(32752.0h)"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Float) {
-    WrapInFunction(Call<f32>(-1.2e-5_f));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("-0.00001200000042445026f"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<f16>(-1.2e-3_h));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("float16_t(-0.0011997222900390625h)"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Bool) {
-    WrapInFunction(Call<bool>(true));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("true"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Int) {
-    WrapInFunction(Call<i32>(-12345_i));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("-12345"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Uint) {
-    WrapInFunction(Call<u32>(12345_u));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("12345u"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_F32) {
-    WrapInFunction(Call<vec3<f32>>(1_f, 2_f, 3_f));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("float3(1.0f, 2.0f, 3.0f)"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<vec3<f16>>(1_h, 2_h, 3_h));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(
-        gen.Result(),
-        HasSubstr("vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h))"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_Empty_F32) {
-    WrapInFunction(Call<vec3<f32>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("0.0f).xxx"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_Empty_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<vec3<f16>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("(float16_t(0.0h)).xxx"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_SingleScalar_F32_Literal) {
-    WrapInFunction(Call<vec3<f32>>(2_f));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("2.0f).xxx"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_SingleScalar_F16_Literal) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<vec3<f16>>(2_h));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("(float16_t(2.0h)).xxx"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_SingleScalar_F32_Var) {
-    auto* var = Var("v", Expr(2_f));
-    auto* cast = Call<vec3<f32>>(var);
-    WrapInFunction(var, cast);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(float v = 2.0f;
-  float3 tint_symbol = float3((v).xxx);)"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_SingleScalar_F16_Var) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = Var("v", Expr(2_h));
-    auto* cast = Call<vec3<f16>>(var);
-    WrapInFunction(var, cast);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(float16_t v = float16_t(2.0h);
-  vector<float16_t, 3> tint_symbol = vector<float16_t, 3>((v).xxx);)"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_SingleScalar_Bool_Literal) {
-    WrapInFunction(Call<vec3<bool>>(true));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("(true).xxx"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_SingleScalar_Bool_Var) {
-    auto* var = Var("v", Expr(true));
-    auto* cast = Call<vec3<bool>>(var);
-    WrapInFunction(var, cast);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(bool v = true;
-  bool3 tint_symbol = bool3((v).xxx);)"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_SingleScalar_Int) {
-    WrapInFunction(Call<vec3<i32>>(2_i));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("2).xxx"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Vec_SingleScalar_UInt) {
-    WrapInFunction(Call<vec3<u32>>(2_u));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("2u).xxx"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Mat_F32) {
-    WrapInFunction(
-        Call<mat2x3<f32>>(Call<vec3<f32>>(1_f, 2_f, 3_f), Call<vec3<f32>>(3_f, 4_f, 5_f)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(),
-                HasSubstr("float2x3(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f))"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Mat_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(
-        Call<mat2x3<f16>>(Call<vec3<f16>>(1_h, 2_h, 3_h), Call<vec3<f16>>(3_h, 4_h, 5_h)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(),
-                HasSubstr("matrix<float16_t, 2, 3>(vector<float16_t, 3>(float16_t(1.0h), "
-                          "float16_t(2.0h), float16_t(3.0h)), vector<float16_t, "
-                          "3>(float16_t(3.0h), float16_t(4.0h), float16_t(5.0h)))"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Mat_Complex_F32) {
-    // mat4x4<f32>(
-    //     vec4<f32>(2.0f, 3.0f, 4.0f, 8.0f),
-    //     vec4<f32>(),
-    //     vec4<f32>(7.0f),
-    //     vec4<f32>(vec4<f32>(42.0f, 21.0f, 6.0f, -5.0f)),
-    //   );
-    auto* vector_literal = Call<vec4<f32>>(f32(2.0), f32(3.0), f32(4.0), f32(8.0));
-    auto* vector_zero_init = Call<vec4<f32>>();
-    auto* vector_single_scalar_init = Call<vec4<f32>>(f32(7.0));
-    auto* vector_identical_init =
-        Call<vec4<f32>>(Call<vec4<f32>>(f32(42.0), f32(21.0), f32(6.0), f32(-5.0)));
-
-    auto* constructor = Call<mat4x4<f32>>(vector_literal, vector_zero_init,
-                                          vector_single_scalar_init, vector_identical_init);
-
-    WrapInFunction(constructor);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(), HasSubstr("float4x4(float4(2.0f, 3.0f, 4.0f, 8.0f), (0.0f).xxxx, "
-                                        "(7.0f).xxxx, float4(42.0f, 21.0f, 6.0f, -5.0f))"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Mat_Complex_F16) {
-    // mat4x4<f16>(
-    //     vec4<f16>(2.0h, 3.0h, 4.0h, 8.0h),
-    //     vec4<f16>(),
-    //     vec4<f16>(7.0h),
-    //     vec4<f16>(vec4<f16>(42.0h, 21.0h, 6.0h, -5.0h)),
-    //   );
-    Enable(wgsl::Extension::kF16);
-
-    auto* vector_literal = Call<vec4<f16>>(f16(2.0), f16(3.0), f16(4.0), f16(8.0));
-    auto* vector_zero_init = Call<vec4<f16>>();
-    auto* vector_single_scalar_init = Call<vec4<f16>>(f16(7.0));
-    auto* vector_identical_init =
-        Call<vec4<f16>>(Call<vec4<f16>>(f16(42.0), f16(21.0), f16(6.0), f16(-5.0)));
-
-    auto* constructor = Call<mat4x4<f16>>(vector_literal, vector_zero_init,
-                                          vector_single_scalar_init, vector_identical_init);
-
-    WrapInFunction(constructor);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(), HasSubstr("matrix<float16_t, 4, 4>("
-                                        "vector<float16_t, 4>(float16_t(2.0h), float16_t(3.0h), "
-                                        "float16_t(4.0h), float16_t(8.0h)), "
-                                        "(float16_t(0.0h)).xxxx, (float16_t(7.0h)).xxxx, "
-                                        "vector<float16_t, 4>(float16_t(42.0h), float16_t(21.0h), "
-                                        "float16_t(6.0h), float16_t(-5.0h)))"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Mat_Empty_F32) {
-    WrapInFunction(Call<mat2x3<f32>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(), HasSubstr("float2x3 tint_symbol = float2x3((0.0f).xxx, (0.0f).xxx)"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Mat_Empty_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    WrapInFunction(Call<mat2x3<f16>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(),
-                HasSubstr("matrix<float16_t, 2, 3>((float16_t(0.0h)).xxx, (float16_t(0.0h)).xxx)"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Mat_Identity_F32) {
-    // fn f() {
-    //     var m_1: mat4x4<f32> = mat4x4<f32>();
-    //     var m_2: mat4x4<f32> = mat4x4<f32>(m_1);
-    // }
-
-    auto* m_1 = Var("m_1", ty.mat4x4(ty.f32()), Call<mat4x4<f32>>());
-    auto* m_2 = Var("m_2", ty.mat4x4(ty.f32()), Call<mat4x4<f32>>(m_1));
-
-    WrapInFunction(m_1, m_2);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(), HasSubstr("float4x4 m_2 = float4x4(m_1);"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Mat_Identity_F16) {
-    // fn f() {
-    //     var m_1: mat4x4<f16> = mat4x4<f16>();
-    //     var m_2: mat4x4<f16> = mat4x4<f16>(m_1);
-    // }
-
-    Enable(wgsl::Extension::kF16);
-
-    auto* m_1 = Var("m_1", ty.mat4x4(ty.f16()), Call<mat4x4<f16>>());
-    auto* m_2 = Var("m_2", ty.mat4x4(ty.f16()), Call<mat4x4<f16>>(m_1));
-
-    WrapInFunction(m_1, m_2);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_THAT(gen.Result(),
-                HasSubstr("matrix<float16_t, 4, 4> m_2 = matrix<float16_t, 4, 4>(m_1);"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Array) {
-    WrapInFunction(Call<array<vec3<f32>, 3>>(Call<vec3<f32>>(1_f, 2_f, 3_f),
-                                             Call<vec3<f32>>(4_f, 5_f, 6_f),
-                                             Call<vec3<f32>>(7_f, 8_f, 9_f)));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(
-        gen.Result(),
-        HasSubstr(
-            "{float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f)}"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Array_Empty) {
-    WrapInFunction(Call<array<vec3<f32>, 3>>());
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("(float3[3])0"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Struct) {
-    auto* str = Structure("S", Vector{
-                                   Member("a", ty.i32()),
-                                   Member("b", ty.f32()),
-                                   Member("c", ty.vec3<i32>()),
-                               });
-
-    WrapInFunction(Call(ty.Of(str), 1_i, 2_f, Call<vec3<i32>>(3_i, 4_i, 5_i)));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("{1, 2.0f, int3(3, 4, 5)}"));
-}
-
-TEST_F(HlslASTPrinterTest_Constructor, Type_Struct_Empty) {
-    auto* str = Structure("S", Vector{
-                                   Member("a", ty.i32()),
-                                   Member("b", ty.f32()),
-                                   Member("c", ty.vec3<i32>()),
-                               });
-
-    WrapInFunction(Call(ty.Of(str)));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("(S)0"));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/continue_test.cc b/src/tint/lang/hlsl/writer/ast_printer/continue_test.cc
deleted file mode 100644
index 0581c1f..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/continue_test.cc
+++ /dev/null
@@ -1,55 +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/hlsl/writer/ast_printer/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Continue = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Continue, Emit_Continue) {
-    auto* loop = Loop(Block(If(false, Block(Break())),  //
-                            Continue()));
-    WrapInFunction(loop);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while (true) {
-    if (false) {
-      break;
-    }
-    continue;
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/discard_test.cc b/src/tint/lang/hlsl/writer/ast_printer/discard_test.cc
deleted file mode 100644
index 0d06a09..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/discard_test.cc
+++ /dev/null
@@ -1,49 +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/hlsl/writer/ast_printer/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Discard = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Discard, Emit_Discard) {
-    auto* stmt = Discard();
-
-    Func("F", tint::Empty, ty.void_(), Vector{stmt}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  discard;\n");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/function_test.cc b/src/tint/lang/hlsl/writer/ast_printer/function_test.cc
deleted file mode 100644
index 2575346..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/function_test.cc
+++ /dev/null
@@ -1,895 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-#include "src/tint/lang/wgsl/ast/workgroup_attribute.h"
-
-using ::testing::HasSubstr;
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using HlslASTPrinterTest_Function = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Function) {
-    Func("my_func", tint::Empty, ty.void_(),
-         Vector{
-             Return(),
-         });
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  void my_func() {
-    return;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Function_Name_Collision) {
-    Func("GeometryShader", tint::Empty, ty.void_(),
-         Vector{
-             Return(),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(  void tint_symbol() {
-    return;
-  })"));
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Function_WithParams) {
-    Func("my_func",
-         Vector{
-             Param("a", ty.f32()),
-             Param("b", ty.i32()),
-         },
-         ty.void_(),
-         Vector{
-             Return(),
-         });
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  void my_func(float a, int b) {
-    return;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_NoReturn_Void) {
-    Func("main", tint::Empty, ty.void_(), tint::Empty /* no explicit return */,
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(void main() {
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, PtrParameter) {
-    // fn f(foo : ptr<function, f32>) -> f32 {
-    //   return *foo;
-    // }
-    Func("f", Vector{Param("foo", ty.ptr<function, f32>())}, ty.f32(),
-         Vector{Return(Deref("foo"))});
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(float f(inout float foo) {
-  return foo;
-}
-)"));
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_WithInOutVars) {
-    // fn frag_main(@location(0) foo : f32) -> @location(1) f32 {
-    //   return foo;
-    // }
-    auto* foo_in = Param("foo", ty.f32(), Vector{Location(0_a)});
-    Func("frag_main", Vector{foo_in}, ty.f32(),
-         Vector{
-             Return("foo"),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         },
-         Vector{
-             Location(1_a),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct tint_symbol_1 {
-  float foo : TEXCOORD0;
-};
-struct tint_symbol_2 {
-  float value : SV_Target1;
-};
-
-float frag_main_inner(float foo) {
-  return foo;
-}
-
-tint_symbol_2 frag_main(tint_symbol_1 tint_symbol) {
-  float inner_result = frag_main_inner(tint_symbol.foo);
-  tint_symbol_2 wrapper_result = (tint_symbol_2)0;
-  wrapper_result.value = inner_result;
-  return wrapper_result;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_WithInOut_Builtins) {
-    // fn frag_main(@position(0) coord : vec4<f32>) -> @frag_depth f32 {
-    //   return coord.x;
-    // }
-    auto* coord_in = Param("coord", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kPosition)});
-    Func("frag_main", Vector{coord_in}, ty.f32(),
-         Vector{
-             Return(MemberAccessor("coord", "x")),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         },
-         Vector{
-             Builtin(core::BuiltinValue::kFragDepth),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct tint_symbol_1 {
-  float4 coord : SV_Position;
-};
-struct tint_symbol_2 {
-  float value : SV_Depth;
-};
-
-float frag_main_inner(float4 coord) {
-  return coord.x;
-}
-
-tint_symbol_2 frag_main(tint_symbol_1 tint_symbol) {
-  float inner_result = frag_main_inner(float4(tint_symbol.coord.xyz, (1.0f / tint_symbol.coord.w)));
-  tint_symbol_2 wrapper_result = (tint_symbol_2)0;
-  wrapper_result.value = inner_result;
-  return wrapper_result;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_SharedStruct_DifferentStages) {
-    // struct Interface {
-    //   @builtin(position) pos : vec4<f32>;
-    //   @location(1) col1 : f32;
-    //   @location(2) col2 : f32;
-    // };
-    // fn vert_main() -> Interface {
-    //   return Interface(vec4<f32>(), 0.4, 0.6);
-    // }
-    // fn frag_main(inputs : Interface) {
-    //   const r = inputs.col1;
-    //   const g = inputs.col2;
-    //   const p = inputs.pos;
-    // }
-    auto* interface_struct =
-        Structure("Interface",
-                  Vector{
-                      Member("pos", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kPosition)}),
-                      Member("col1", ty.f32(), Vector{Location(1_a)}),
-                      Member("col2", ty.f32(), Vector{Location(2_a)}),
-                  });
-
-    Func("vert_main", tint::Empty, ty.Of(interface_struct),
-         Vector{
-             Return(Call(ty.Of(interface_struct), Call<vec4<f32>>(), 0.5_f, 0.25_f)),
-         },
-         Vector{Stage(ast::PipelineStage::kVertex)});
-
-    Func("frag_main", Vector{Param("inputs", ty.Of(interface_struct))}, ty.void_(),
-         Vector{
-             Decl(Let("r", ty.f32(), MemberAccessor("inputs", "col1"))),
-             Decl(Let("g", ty.f32(), MemberAccessor("inputs", "col2"))),
-             Decl(Let("p", ty.vec4<f32>(), MemberAccessor("inputs", "pos"))),
-         },
-         Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct Interface {
-  float4 pos;
-  float col1;
-  float col2;
-};
-struct tint_symbol {
-  float col1 : TEXCOORD1;
-  float col2 : TEXCOORD2;
-  float4 pos : SV_Position;
-};
-
-Interface vert_main_inner() {
-  Interface tint_symbol_3 = {(0.0f).xxxx, 0.5f, 0.25f};
-  return tint_symbol_3;
-}
-
-tint_symbol vert_main() {
-  Interface inner_result = vert_main_inner();
-  tint_symbol wrapper_result = (tint_symbol)0;
-  wrapper_result.pos = inner_result.pos;
-  wrapper_result.col1 = inner_result.col1;
-  wrapper_result.col2 = inner_result.col2;
-  return wrapper_result;
-}
-
-struct tint_symbol_2 {
-  float col1 : TEXCOORD1;
-  float col2 : TEXCOORD2;
-  float4 pos : SV_Position;
-};
-
-void frag_main_inner(Interface inputs) {
-  float r = inputs.col1;
-  float g = inputs.col2;
-  float4 p = inputs.pos;
-}
-
-void frag_main(tint_symbol_2 tint_symbol_1) {
-  Interface tint_symbol_4 = {float4(tint_symbol_1.pos.xyz, (1.0f / tint_symbol_1.pos.w)), tint_symbol_1.col1, tint_symbol_1.col2};
-  frag_main_inner(tint_symbol_4);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_SharedStruct_HelperFunction) {
-    // struct VertexOutput {
-    //   @builtin(position) pos : vec4<f32>;
-    // };
-    // fn foo(x : f32) -> VertexOutput {
-    //   return VertexOutput(vec4<f32>(x, x, x, 1.0));
-    // }
-    // fn vert_main1() -> VertexOutput {
-    //   return foo(0.5);
-    // }
-    // fn vert_main2() -> VertexOutput {
-    //   return foo(0.25);
-    // }
-    auto* vertex_output_struct = Structure(
-        "VertexOutput",
-        Vector{Member("pos", ty.vec4<f32>(), Vector{Builtin(core::BuiltinValue::kPosition)})});
-
-    Func("foo", Vector{Param("x", ty.f32())}, ty.Of(vertex_output_struct),
-         Vector{
-             Return(Call(ty.Of(vertex_output_struct), Call<vec4<f32>>("x", "x", "x", 1_f))),
-         },
-         tint::Empty);
-
-    Func("vert_main1", tint::Empty, ty.Of(vertex_output_struct),
-         Vector{
-             Return(Call("foo", Expr(0.5_f))),
-         },
-         Vector{Stage(ast::PipelineStage::kVertex)});
-
-    Func("vert_main2", tint::Empty, ty.Of(vertex_output_struct),
-         Vector{
-             Return(Call("foo", Expr(0.25_f))),
-         },
-         Vector{Stage(ast::PipelineStage::kVertex)});
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct VertexOutput {
-  float4 pos;
-};
-
-VertexOutput foo(float x) {
-  VertexOutput tint_symbol_2 = {float4(x, x, x, 1.0f)};
-  return tint_symbol_2;
-}
-
-struct tint_symbol {
-  float4 pos : SV_Position;
-};
-
-VertexOutput vert_main1_inner() {
-  return foo(0.5f);
-}
-
-tint_symbol vert_main1() {
-  VertexOutput inner_result = vert_main1_inner();
-  tint_symbol wrapper_result = (tint_symbol)0;
-  wrapper_result.pos = inner_result.pos;
-  return wrapper_result;
-}
-
-struct tint_symbol_1 {
-  float4 pos : SV_Position;
-};
-
-VertexOutput vert_main2_inner() {
-  return foo(0.25f);
-}
-
-tint_symbol_1 vert_main2() {
-  VertexOutput inner_result_1 = vert_main2_inner();
-  tint_symbol_1 wrapper_result_1 = (tint_symbol_1)0;
-  wrapper_result_1.pos = inner_result_1.pos;
-  return wrapper_result_1;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_With_Uniform) {
-    auto* ubo_ty = Structure("UBO", Vector{Member("coord", ty.vec4<f32>())});
-    auto* ubo =
-        GlobalVar("ubo", ty.Of(ubo_ty), core::AddressSpace::kUniform, Binding(0_a), Group(1_a));
-
-    Func("sub_func",
-         Vector{
-             Param("param", ty.f32()),
-         },
-         ty.f32(),
-         Vector{
-             Return(MemberAccessor(MemberAccessor(ubo, "coord"), "x")),
-         });
-
-    auto* var = Var("v", ty.f32(), Call("sub_func", 1_f));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(cbuffer cbuffer_ubo : register(b0, space1) {
-  uint4 ubo[1];
-};
-
-float sub_func(float param) {
-  return asfloat(ubo[0].x);
-}
-
-void frag_main() {
-  float v = sub_func(1.0f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_With_UniformStruct) {
-    auto* s = Structure("Uniforms", Vector{Member("coord", ty.vec4<f32>())});
-
-    GlobalVar("uniforms", ty.Of(s), core::AddressSpace::kUniform, Binding(0_a), Group(1_a));
-
-    auto* var = Var("v", ty.f32(), MemberAccessor(MemberAccessor("uniforms", "coord"), "x"));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(cbuffer cbuffer_uniforms : register(b0, space1) {
-  uint4 uniforms[1];
-};
-
-void frag_main() {
-  float v = uniforms.coord.x;
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_With_RW_StorageBuffer_Read) {
-    auto* s = Structure("Data", Vector{
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.f32()),
-                                });
-
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite,
-              Binding(0_a), Group(1_a));
-
-    auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(RWByteAddressBuffer coord : register(u0, space1);
-
-void frag_main() {
-  float v = asfloat(coord.Load(4u));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_With_RO_StorageBuffer_Read) {
-    auto* s = Structure("Data", Vector{
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.f32()),
-                                });
-
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(0_a),
-              Group(1_a));
-
-    auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(ByteAddressBuffer coord : register(t0, space1);
-
-void frag_main() {
-  float v = asfloat(coord.Load(4u));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_With_WO_StorageBuffer_Store) {
-    auto* s = Structure("Data", Vector{
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.f32()),
-                                });
-
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite,
-              Binding(0_a), Group(1_a));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Assign(MemberAccessor("coord", "b"), Expr(2_f)),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(RWByteAddressBuffer coord : register(u0, space1);
-
-void frag_main() {
-  coord.Store(4u, asuint(2.0f));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_With_StorageBuffer_Store) {
-    auto* s = Structure("Data", Vector{
-                                    Member("a", ty.i32()),
-                                    Member("b", ty.f32()),
-                                });
-
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite,
-              Binding(0_a), Group(1_a));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Assign(MemberAccessor("coord", "b"), Expr(2_f)),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(RWByteAddressBuffer coord : register(u0, space1);
-
-void frag_main() {
-  coord.Store(4u, asuint(2.0f));
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
-    auto* s = Structure("S", Vector{Member("x", ty.f32())});
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kUniform, Binding(0_a), Group(1_a));
-
-    Func("sub_func",
-         Vector{
-             Param("param", ty.f32()),
-         },
-         ty.f32(),
-         Vector{
-             Return(MemberAccessor("coord", "x")),
-         });
-
-    auto* var = Var("v", ty.f32(), Call("sub_func", 1_f));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(cbuffer cbuffer_coord : register(b0, space1) {
-  uint4 coord[1];
-};
-
-float sub_func(float param) {
-  return coord.x;
-}
-
-void frag_main() {
-  float v = sub_func(1.0f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_Called_By_EntryPoint_With_StorageBuffer) {
-    auto* s = Structure("S", Vector{Member("x", ty.f32())});
-    GlobalVar("coord", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite,
-              Binding(0_a), Group(1_a));
-
-    Func("sub_func",
-         Vector{
-             Param("param", ty.f32()),
-         },
-         ty.f32(),
-         Vector{
-             Return(MemberAccessor("coord", "x")),
-         });
-
-    auto* var = Var("v", ty.f32(), Call("sub_func", 1_f));
-
-    Func("frag_main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(var),
-             Return(),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(RWByteAddressBuffer coord : register(u0, space1);
-
-float sub_func(float param) {
-  return asfloat(coord.Load(0u));
-}
-
-void frag_main() {
-  float v = sub_func(1.0f);
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_WithNameCollision) {
-    Func("GeometryShader", tint::Empty, ty.void_(), tint::Empty,
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(void tint_symbol() {
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_Compute) {
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Return(),
-         },
-         Vector{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(1, 1, 1)]
-void main() {
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Literal) {
-    Func("main", tint::Empty, ty.void_(), tint::Empty,
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(2_i, 4_i, 6_i),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(2, 4, 6)]
-void main() {
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Attribute_EntryPoint_Compute_WithWorkgroup_Const) {
-    GlobalConst("width", ty.i32(), Call<i32>(2_i));
-    GlobalConst("height", ty.i32(), Call<i32>(3_i));
-    GlobalConst("depth", ty.i32(), Call<i32>(4_i));
-    Func("main", tint::Empty, ty.void_(), tint::Empty,
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize("width", "height", "depth"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"([numthreads(2, 3, 4)]
-void main() {
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function,
-       Emit_Attribute_EntryPoint_Compute_WithWorkgroup_OverridableConst) {
-    Override("width", ty.i32(), Call<i32>(2_i), Id(7_u));
-    Override("height", ty.i32(), Call<i32>(3_i), Id(8_u));
-    Override("depth", ty.i32(), Call<i32>(4_i), Id(9_u));
-    Func("main", tint::Empty, ty.void_(), tint::Empty,
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize("width", "height", "depth"),
-         });
-
-    ASTPrinter& gen = Build();
-
-    EXPECT_FALSE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(
-        gen.Diagnostics().Str(),
-        R"(error: override-expressions should have been removed with the SubstituteOverride transform)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Function_WithArrayParams) {
-    Func("my_func",
-         Vector{
-             Param("a", ty.array<f32, 5>()),
-         },
-         ty.void_(),
-         Vector{
-             Return(),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(void my_func(float a[5]) {
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Function_WithArrayReturn) {
-    Func("my_func", tint::Empty, ty.array<f32, 5>(),
-         Vector{
-             Return(Call<array<f32, 5>>()),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(typedef float my_func_ret[5];
-my_func_ret my_func() {
-  return (float[5])0;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Function_WithDiscardAndVoidReturn) {
-    Func("my_func", Vector{Param("a", ty.i32())}, ty.void_(),
-         Vector{
-             If(Equal("a", 0_i),  //
-                Block(Discard())),
-             Return(),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(void my_func(int a) {
-  if ((a == 0)) {
-    discard;
-  }
-  return;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Function, Emit_Function_WithDiscardAndNonVoidReturn) {
-    Func("my_func", Vector{Param("a", ty.i32())}, ty.i32(),
-         Vector{
-             If(Equal("a", 0_i),  //
-                Block(Discard())),
-             Return(42_i),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(int my_func(int a) {
-  if (true) {
-    if ((a == 0)) {
-      discard;
-    }
-    return 42;
-  }
-  int unused;
-  return unused;
-}
-)");
-}
-
-// https://crbug.com/tint/297
-TEST_F(HlslASTPrinterTest_Function, Emit_Multiple_EntryPoint_With_Same_ModuleVar) {
-    // struct Data {
-    //   d : f32;
-    // };
-    // @binding(0) @group(0) var<storage> data : Data;
-    //
-    // @compute @workgroup_size(1)
-    // fn a() {
-    //   var v = data.d;
-    //   return;
-    // }
-    //
-    // @compute @workgroup_size(1)
-    // fn b() {
-    //   var v = data.d;
-    //   return;
-    // }
-
-    auto* s = Structure("Data", Vector{Member("d", ty.f32())});
-
-    GlobalVar("data", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite,
-              Binding(0_a), Group(0_a));
-
-    {
-        auto* var = Var("v", ty.f32(), MemberAccessor("data", "d"));
-
-        Func("a", tint::Empty, ty.void_(),
-             Vector{
-                 Decl(var),
-                 Return(),
-             },
-             Vector{
-                 Stage(ast::PipelineStage::kCompute),
-                 WorkgroupSize(1_i),
-             });
-    }
-
-    {
-        auto* var = Var("v", ty.f32(), MemberAccessor("data", "d"));
-
-        Func("b", tint::Empty, ty.void_(),
-             Vector{
-                 Decl(var),
-                 Return(),
-             },
-             Vector{
-                 Stage(ast::PipelineStage::kCompute),
-                 WorkgroupSize(1_i),
-             });
-    }
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(RWByteAddressBuffer data : register(u0);
-
-[numthreads(1, 1, 1)]
-void a() {
-  float v = asfloat(data.Load(0u));
-  return;
-}
-
-[numthreads(1, 1, 1)]
-void b() {
-  float v = asfloat(data.Load(0u));
-  return;
-}
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/helper_test.h b/src/tint/lang/hlsl/writer/ast_printer/helper_test.h
deleted file mode 100644
index 800d85d..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/helper_test.h
+++ /dev/null
@@ -1,134 +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_HLSL_WRITER_AST_PRINTER_HELPER_TEST_H_
-#define SRC_TINT_LANG_HLSL_WRITER_AST_PRINTER_HELPER_TEST_H_
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "gtest/gtest.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/ast_printer.h"
-#include "src/tint/lang/hlsl/writer/common/options.h"
-#include "src/tint/lang/wgsl/ast/transform/manager.h"
-#include "src/tint/lang/wgsl/ast/transform/renamer.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-
-namespace tint::hlsl::writer {
-
-/// Helper class for testing
-template <typename BODY>
-class TestHelperBase : public BODY, public ProgramBuilder {
-  public:
-    TestHelperBase() = default;
-    ~TestHelperBase() override = default;
-
-    /// @returns the default generator options for SanitizeAndBuild(), if no explicit options are
-    /// provided.
-    static Options DefaultOptions() {
-        Options opts;
-        opts.disable_robustness = true;
-        return opts;
-    }
-
-    /// Builds the program and returns a ASTPrinter from the program.
-    /// @note The generator is only built once. Multiple calls to Build() will
-    /// return the same ASTPrinter without rebuilding.
-    /// @return the built generator
-    ASTPrinter& Build() {
-        if (gen_) {
-            return *gen_;
-        }
-        if (!IsValid()) {
-            ADD_FAILURE() << "ProgramBuilder is not valid: " << Diagnostics();
-        }
-        program = std::make_unique<Program>(resolver::Resolve(*this));
-        if (!program->IsValid()) {
-            ADD_FAILURE() << program->Diagnostics();
-        }
-        gen_ = std::make_unique<ASTPrinter>(*program);
-        return *gen_;
-    }
-
-    /// Builds the program, runs the program through the HLSL sanitizer
-    /// and returns a ASTPrinter from the sanitized program.
-    /// @param options The HLSL generator options.
-    /// @note The generator is only built once. Multiple calls to Build() will
-    /// return the same ASTPrinter without rebuilding.
-    /// @return the built generator
-    ASTPrinter& SanitizeAndBuild(const Options& options = DefaultOptions()) {
-        if (gen_) {
-            return *gen_;
-        }
-        if (!IsValid()) {
-            ADD_FAILURE() << "ProgramBuilder is not valid: " << Diagnostics();
-        }
-        program = std::make_unique<Program>(resolver::Resolve(*this));
-        if (!program->IsValid()) {
-            ADD_FAILURE() << program->Diagnostics();
-        }
-
-        auto sanitized_result = Sanitize(*program, options);
-        if (!sanitized_result.program.IsValid()) {
-            ADD_FAILURE() << sanitized_result.program.Diagnostics();
-        }
-
-        ast::transform::Manager transform_manager;
-        ast::transform::DataMap transform_data;
-        ast::transform::DataMap outputs;
-        transform_data.Add<ast::transform::Renamer::Config>(
-            ast::transform::Renamer::Target::kHlslKeywords);
-        transform_manager.Add<tint::ast::transform::Renamer>();
-        auto result = transform_manager.Run(sanitized_result.program, transform_data, outputs);
-        if (!result.IsValid()) {
-            ADD_FAILURE() << result.Diagnostics();
-        }
-        *program = std::move(result);
-        gen_ = std::make_unique<ASTPrinter>(*program);
-        return *gen_;
-    }
-
-    /// The program built with a call to Build()
-    std::unique_ptr<Program> program;
-
-  private:
-    std::unique_ptr<ASTPrinter> gen_;
-};
-
-/// TestHelper the base class for HLSL writer unit tests.
-/// Use this form when you don't need to template any further.
-using TestHelper = TestHelperBase<testing::Test>;
-
-/// TestParamHelper the base class for HLSL unit tests that take a templated
-/// parameter.
-template <typename T>
-using TestParamHelper = TestHelperBase<testing::TestWithParam<T>>;
-
-}  // namespace tint::hlsl::writer
-
-#endif  // SRC_TINT_LANG_HLSL_WRITER_AST_PRINTER_HELPER_TEST_H_
diff --git a/src/tint/lang/hlsl/writer/ast_printer/identifier_test.cc b/src/tint/lang/hlsl/writer/ast_printer/identifier_test.cc
deleted file mode 100644
index 46bb0f9..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/identifier_test.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Identifier = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Identifier, EmitIdentifierExpression) {
-    GlobalVar("foo", ty.i32(), core::AddressSpace::kPrivate);
-
-    auto* i = Expr("foo");
-    WrapInFunction(i);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, i)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "foo");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/if_test.cc b/src/tint/lang/hlsl/writer/ast_printer/if_test.cc
deleted file mode 100644
index b5a5a63..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/if_test.cc
+++ /dev/null
@@ -1,136 +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/hlsl/writer/ast_printer/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_If = TestHelper;
-
-TEST_F(HlslASTPrinterTest_If, Emit_If) {
-    GlobalVar("cond", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* cond = Expr("cond");
-    auto* body = Block(Return());
-    auto* i = If(cond, body);
-    WrapInFunction(i);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  if (cond) {
-    return;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_If, Emit_IfWithElseIf) {
-    GlobalVar("cond", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("else_cond", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* else_cond = Expr("else_cond");
-    auto* else_body = Block(Return());
-
-    auto* cond = Expr("cond");
-    auto* body = Block(Return());
-    auto* i = If(cond, body, Else(If(else_cond, else_body)));
-    WrapInFunction(i);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  if (cond) {
-    return;
-  } else {
-    if (else_cond) {
-      return;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_If, Emit_IfWithElse) {
-    GlobalVar("cond", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* else_body = Block(Return());
-
-    auto* cond = Expr("cond");
-    auto* body = Block(Return());
-    auto* i = If(cond, body, Else(else_body));
-    WrapInFunction(i);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  if (cond) {
-    return;
-  } else {
-    return;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_If, Emit_IfWithMultiple) {
-    GlobalVar("cond", ty.bool_(), core::AddressSpace::kPrivate);
-    GlobalVar("else_cond", ty.bool_(), core::AddressSpace::kPrivate);
-
-    auto* else_cond = Expr("else_cond");
-
-    auto* else_body = Block(Return());
-
-    auto* else_body_2 = Block(Return());
-
-    auto* cond = Expr("cond");
-    auto* body = Block(Return());
-    auto* i = If(cond, body, Else(If(else_cond, else_body, Else(else_body_2))));
-    WrapInFunction(i);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(i)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  if (cond) {
-    return;
-  } else {
-    if (else_cond) {
-      return;
-    } else {
-      return;
-    }
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/import_test.cc b/src/tint/lang/hlsl/writer/ast_printer/import_test.cc
deleted file mode 100644
index fa3cb3d..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/import_test.cc
+++ /dev/null
@@ -1,309 +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/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using HlslASTPrinterTest_Import = TestHelper;
-
-struct HlslImportData {
-    const char* name;
-    const char* hlsl_name;
-};
-inline std::ostream& operator<<(std::ostream& out, HlslImportData data) {
-    out << data.name;
-    return out;
-}
-
-using HlslImportData_SingleParamTest = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_SingleParamTest, FloatScalar) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, 1_f);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1.0f)");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_SingleParamTest,
-                         testing::Values(HlslImportData{"abs", "abs"},
-                                         HlslImportData{"acos", "acos"},
-                                         HlslImportData{"asin", "asin"},
-                                         HlslImportData{"atan", "atan"},
-                                         HlslImportData{"cos", "cos"},
-                                         HlslImportData{"cosh", "cosh"},
-                                         HlslImportData{"ceil", "ceil"},
-                                         HlslImportData{"exp", "exp"},
-                                         HlslImportData{"exp2", "exp2"},
-                                         HlslImportData{"floor", "floor"},
-                                         HlslImportData{"fract", "frac"},
-                                         HlslImportData{"inverseSqrt", "rsqrt"},
-                                         HlslImportData{"length", "length"},
-                                         HlslImportData{"log", "log"},
-                                         HlslImportData{"log2", "log2"},
-                                         HlslImportData{"round", "round"},
-                                         HlslImportData{"sin", "sin"},
-                                         HlslImportData{"sinh", "sinh"},
-                                         HlslImportData{"sqrt", "sqrt"},
-                                         HlslImportData{"tan", "tan"},
-                                         HlslImportData{"tanh", "tanh"}));
-
-using HlslImportData_SingleIntParamTest = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_SingleIntParamTest, IntScalar) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, Expr(1_i));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1)");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_SingleIntParamTest,
-                         testing::Values(HlslImportData{"abs", "abs"}));
-
-using HlslImportData_SingleVectorParamTest = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_SingleVectorParamTest, FloatVector) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, Call<vec3<f32>>(0.1_f, 0.2_f, 0.3_f));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(
-        out.str(),
-        std::string(param.hlsl_name) +
-            "(float3(0.10000000149011611938f, 0.20000000298023223877f, 0.30000001192092895508f))");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_SingleVectorParamTest,
-                         testing::Values(HlslImportData{"abs", "abs"},
-                                         HlslImportData{"acos", "acos"},
-                                         HlslImportData{"asin", "asin"},
-                                         HlslImportData{"atan", "atan"},
-                                         HlslImportData{"cos", "cos"},
-                                         HlslImportData{"cosh", "cosh"},
-                                         HlslImportData{"ceil", "ceil"},
-                                         HlslImportData{"exp", "exp"},
-                                         HlslImportData{"exp2", "exp2"},
-                                         HlslImportData{"floor", "floor"},
-                                         HlslImportData{"fract", "frac"},
-                                         HlslImportData{"inverseSqrt", "rsqrt"},
-                                         HlslImportData{"length", "length"},
-                                         HlslImportData{"log", "log"},
-                                         HlslImportData{"log2", "log2"},
-                                         HlslImportData{"normalize", "normalize"},
-                                         HlslImportData{"round", "round"},
-                                         HlslImportData{"sin", "sin"},
-                                         HlslImportData{"sinh", "sinh"},
-                                         HlslImportData{"sqrt", "sqrt"},
-                                         HlslImportData{"tan", "tan"},
-                                         HlslImportData{"tanh", "tanh"}));
-
-using HlslImportData_DualParam_ScalarTest = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_DualParam_ScalarTest, Float) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, 1_f, 2_f);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1.0f, 2.0f)");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_DualParam_ScalarTest,
-                         testing::Values(HlslImportData{"atan2", "atan2"},
-                                         HlslImportData{"distance", "distance"},
-                                         HlslImportData{"max", "max"},
-                                         HlslImportData{"min", "min"},
-                                         HlslImportData{"pow", "pow"},
-                                         HlslImportData{"step", "step"}));
-
-using HlslImportData_DualParam_VectorTest = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_DualParam_VectorTest, Float) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, Call<vec3<f32>>(1_f, 2_f, 3_f), Call<vec3<f32>>(4_f, 5_f, 6_f));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.hlsl_name) +
-                             "(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_DualParam_VectorTest,
-                         testing::Values(HlslImportData{"atan2", "atan2"},
-                                         HlslImportData{"cross", "cross"},
-                                         HlslImportData{"distance", "distance"},
-                                         HlslImportData{"max", "max"},
-                                         HlslImportData{"min", "min"},
-                                         HlslImportData{"pow", "pow"},
-                                         HlslImportData{"reflect", "reflect"},
-                                         HlslImportData{"step", "step"}));
-
-using HlslImportData_DualParam_Int_Test = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_DualParam_Int_Test, IntScalar) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, 1_i, 2_i);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1, 2)");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_DualParam_Int_Test,
-                         testing::Values(HlslImportData{"max", "max"},
-                                         HlslImportData{"min", "min"}));
-
-using HlslImportData_TripleParam_ScalarTest = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_TripleParam_ScalarTest, Float) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, 1_f, 2_f, 3_f);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1.0f, 2.0f, 3.0f)");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_TripleParam_ScalarTest,
-                         testing::Values(HlslImportData{"fma", "mad"},
-                                         HlslImportData{"mix", "lerp"},
-                                         HlslImportData{"clamp", "clamp"},
-                                         HlslImportData{"smoothstep", "smoothstep"}));
-
-using HlslImportData_TripleParam_VectorTest = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_TripleParam_VectorTest, Float) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, Call<vec3<f32>>(1_f, 2_f, 3_f), Call<vec3<f32>>(4_f, 5_f, 6_f),
-                      Call<vec3<f32>>(7_f, 8_f, 9_f));
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(
-        out.str(),
-        std::string(param.hlsl_name) +
-            R"((float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f)))");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_TripleParam_VectorTest,
-                         testing::Values(HlslImportData{"faceForward", "faceforward"},
-                                         HlslImportData{"fma", "mad"},
-                                         HlslImportData{"clamp", "clamp"},
-                                         HlslImportData{"smoothstep", "smoothstep"}));
-
-using HlslImportData_TripleParam_Int_Test = TestParamHelper<HlslImportData>;
-TEST_P(HlslImportData_TripleParam_Int_Test, IntScalar) {
-    auto param = GetParam();
-
-    auto* expr = Call(param.name, 1_i, 2_i, 3_i);
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string(param.hlsl_name) + "(1, 2, 3)");
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Import,
-                         HlslImportData_TripleParam_Int_Test,
-                         testing::Values(HlslImportData{"clamp", "clamp"}));
-
-TEST_F(HlslASTPrinterTest_Import, HlslImportData_Determinant) {
-    GlobalVar("var", ty.mat3x3<f32>(), core::AddressSpace::kPrivate);
-
-    auto* expr = Call("determinant", "var");
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string("determinant(var)"));
-}
-
-TEST_F(HlslASTPrinterTest_Import, HlslImportData_QuantizeToF16_Scalar) {
-    GlobalVar("v", Expr(2_f), core::AddressSpace::kPrivate);
-
-    auto* expr = Call("quantizeToF16", "v");
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string("f16tof32(f32tof16(v))"));
-}
-
-TEST_F(HlslASTPrinterTest_Import, HlslImportData_QuantizeToF16_Vector) {
-    GlobalVar("v", Call<vec3<f32>>(2_f), core::AddressSpace::kPrivate);
-
-    auto* expr = Call("quantizeToF16", "v");
-    WrapInFunction(expr);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), std::string("f16tof32(f32tof16(v))"));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/loop_test.cc b/src/tint/lang/hlsl/writer/ast_printer/loop_test.cc
deleted file mode 100644
index b855f98..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/loop_test.cc
+++ /dev/null
@@ -1,487 +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/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Loop = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_Loop) {
-    auto* body = Block(Break());
-    auto* continuing = Block();
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while (true) {
-    break;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_LoopWithContinuing) {
-    Func("a_statement", {}, ty.void_(), {});
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")));
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while (true) {
-    break;
-    {
-      a_statement();
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_LoopWithContinuing_BreakIf) {
-    Func("a_statement", {}, ty.void_(), {});
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")), BreakIf(true));
-    auto* l = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{l}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while (true) {
-    break;
-    {
-      a_statement();
-      if (true) { break; }
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_LoopNestedWithContinuing) {
-    Func("a_statement", {}, ty.void_(), {});
-
-    GlobalVar("lhs", ty.f32(), core::AddressSpace::kPrivate);
-    GlobalVar("rhs", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* body = Block(Break());
-    auto* continuing = Block(CallStmt(Call("a_statement")));
-    auto* inner = Loop(body, continuing);
-
-    body = Block(inner);
-
-    auto* lhs = Expr("lhs");
-    auto* rhs = Expr("rhs");
-
-    continuing = Block(Assign(lhs, rhs), BreakIf(true));
-
-    auto* outer = Loop(body, continuing);
-
-    Func("F", tint::Empty, ty.void_(), Vector{outer}, Vector{Stage(ast::PipelineStage::kFragment)});
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while (true) {
-    while (true) {
-      break;
-      {
-        a_statement();
-      }
-    }
-    {
-      lhs = rhs;
-      if (true) { break; }
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_LoopWithVarUsedInContinuing) {
-    // loop {
-    //   var lhs : f32 = 2.5;
-    //   var other : f32;
-    //   break;
-    //   continuing {
-    //     lhs = rhs
-    //   }
-    // }
-
-    GlobalVar("rhs", ty.f32(), core::AddressSpace::kPrivate);
-
-    auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.5_f))),  //
-                       Decl(Var("other", ty.f32())),             //
-                       Break());
-
-    auto* continuing = Block(Assign("lhs", "rhs"));
-    auto* outer = Loop(body, continuing);
-    WrapInFunction(outer);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while (true) {
-    float lhs = 2.5f;
-    float other = 0.0f;
-    break;
-    {
-      lhs = rhs;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoop) {
-    // for(; ; ) {
-    //   return;
-    // }
-
-    auto* f = For(nullptr, nullptr, nullptr, Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    for(; ; ) {
-      return;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoopWithSimpleInit) {
-    // for(var i : i32; ; ) {
-    //   return;
-    // }
-
-    auto* f = For(Decl(Var("i", ty.i32())), nullptr, nullptr, Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    for(int i = 0; ; ) {
-      return;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoopWithMultiStmtInit) {
-    // let t = true;
-    // for(var b = t && false; ; ) {
-    //   return;
-    // }
-
-    auto* t = Let("t", Expr(true));
-    auto* multi_stmt = LogicalAnd(t, false);
-    auto* f = For(Decl(Var("b", multi_stmt)), nullptr, nullptr, Block(Return()));
-    WrapInFunction(t, f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    bool tint_tmp = t;
-    if (tint_tmp) {
-      tint_tmp = false;
-    }
-    bool b = (tint_tmp);
-    for(; ; ) {
-      return;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoopWithSimpleCond) {
-    // for(; true; ) {
-    //   return;
-    // }
-
-    auto* f = For(nullptr, true, nullptr, Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    for(; true; ) {
-      return;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoopWithMultiStmtCond) {
-    // let t = true;
-    // for(; t && false; ) {
-    //   return;
-    // }
-
-    Func("a_statement", {}, ty.void_(), {});
-    auto* t = Let("t", Expr(true));
-    auto* multi_stmt = LogicalAnd(t, false);
-    auto* f = For(nullptr, multi_stmt, nullptr, Block(CallStmt(Call("a_statement"))));
-    WrapInFunction(t, f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    while (true) {
-      bool tint_tmp = t;
-      if (tint_tmp) {
-        tint_tmp = false;
-      }
-      if (!((tint_tmp))) { break; }
-      a_statement();
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoopWithSimpleCont) {
-    // for(; ; i = i + 1i) {
-    //   return;
-    // }
-
-    auto* v = Decl(Var("i", ty.i32()));
-    auto* f = For(nullptr, nullptr, Assign("i", Add("i", 1_i)), Block(Return()));
-    WrapInFunction(v, f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    for(; ; i = (i + 1)) {
-      return;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoopWithMultiStmtCont) {
-    // let t = true;
-    // for(; ; i = t && false) {
-    //   return;
-    // }
-
-    auto* t = Let("t", Expr(true));
-    auto* multi_stmt = LogicalAnd(t, false);
-    auto* v = Decl(Var("i", ty.bool_()));
-    auto* f = For(nullptr, nullptr, Assign("i", multi_stmt),  //
-                  Block(Return()));
-    WrapInFunction(t, v, f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    while (true) {
-      return;
-      bool tint_tmp = t;
-      if (tint_tmp) {
-        tint_tmp = false;
-      }
-      i = (tint_tmp);
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoopWithSimpleInitCondCont) {
-    // for(var i : i32; true; i = i + 1i) {
-    //   return;
-    // }
-
-    auto* f = For(Decl(Var("i", ty.i32())), true, Assign("i", Add("i", 1_i)), Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    for(int i = 0; true; i = (i + 1)) {
-      return;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_ForLoopWithMultiStmtInitCondCont) {
-    // let t = true;
-    // for(var i = t && false; t && false; i = t && false) {
-    //   return;
-    // }
-
-    auto* t = Let("t", Expr(true));
-    auto* multi_stmt_a = LogicalAnd(t, false);
-    auto* multi_stmt_b = LogicalAnd(t, false);
-    auto* multi_stmt_c = LogicalAnd(t, false);
-
-    auto* f = For(Decl(Var("i", multi_stmt_a)), multi_stmt_b, Assign("i", multi_stmt_c),  //
-                  Block(Return()));
-    WrapInFunction(t, f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  {
-    bool tint_tmp = t;
-    if (tint_tmp) {
-      tint_tmp = false;
-    }
-    bool i = (tint_tmp);
-    while (true) {
-      bool tint_tmp_1 = t;
-      if (tint_tmp_1) {
-        tint_tmp_1 = false;
-      }
-      if (!((tint_tmp_1))) { break; }
-      return;
-      bool tint_tmp_2 = t;
-      if (tint_tmp_2) {
-        tint_tmp_2 = false;
-      }
-      i = (tint_tmp_2);
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_While) {
-    // while(true) {
-    //   return;
-    // }
-
-    auto* f = While(Expr(true), Block(Return()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while(true) {
-    return;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_While_WithContinue) {
-    // while(true) {
-    //   continue;
-    // }
-
-    auto* f = While(Expr(true), Block(Continue()));
-    WrapInFunction(f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while(true) {
-    continue;
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Loop, Emit_WhileWithMultiStmtCond) {
-    // let t = true;
-    // while(t && false) {
-    //   return;
-    // }
-
-    Func("a_statement", {}, ty.void_(), {});
-
-    auto* t = Let("t", Expr(true));
-    auto* multi_stmt = LogicalAnd(t, false);
-    auto* f = While(multi_stmt, Block(CallStmt(Call("a_statement"))));
-    WrapInFunction(t, f);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  while (true) {
-    bool tint_tmp = t;
-    if (tint_tmp) {
-      tint_tmp = false;
-    }
-    if (!((tint_tmp))) { break; }
-    a_statement();
-  }
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/member_accessor_test.cc b/src/tint/lang/hlsl/writer/ast_printer/member_accessor_test.cc
deleted file mode 100644
index f06ef68..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/member_accessor_test.cc
+++ /dev/null
@@ -1,2018 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-
-using ::testing::HasSubstr;
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using create_type_func_ptr = ast::Type (*)(const ProgramBuilder::TypesBuilder& ty);
-
-inline ast::Type ty_i32(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.i32();
-}
-inline ast::Type ty_u32(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.u32();
-}
-inline ast::Type ty_f32(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.f32();
-}
-inline ast::Type ty_f16(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.f16();
-}
-template <typename T>
-inline ast::Type ty_vec2(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.vec2<T>();
-}
-template <typename T>
-inline ast::Type ty_vec3(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.vec3<T>();
-}
-template <typename T>
-inline ast::Type ty_vec4(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.vec4<T>();
-}
-template <typename T>
-inline ast::Type ty_mat2x2(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat2x2<T>();
-}
-template <typename T>
-inline ast::Type ty_mat2x3(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat2x3<T>();
-}
-template <typename T>
-inline ast::Type ty_mat2x4(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat2x4<T>();
-}
-template <typename T>
-inline ast::Type ty_mat3x2(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat3x2<T>();
-}
-template <typename T>
-inline ast::Type ty_mat3x3(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat3x3<T>();
-}
-template <typename T>
-inline ast::Type ty_mat3x4(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat3x4<T>();
-}
-template <typename T>
-inline ast::Type ty_mat4x2(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat4x2<T>();
-}
-template <typename T>
-inline ast::Type ty_mat4x3(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat4x3<T>();
-}
-template <typename T>
-inline ast::Type ty_mat4x4(const ProgramBuilder::TypesBuilder& ty) {
-    return ty.mat4x4<T>();
-}
-
-template <typename BASE>
-class HlslASTPrinterTest_MemberAccessorBase : public BASE {
-  public:
-    void SetupStorageBuffer(VectorRef<const ast::StructMember*> members) {
-        ProgramBuilder& b = *this;
-        auto* s = b.Structure("Data", members);
-
-        b.GlobalVar("data", b.ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite,
-                    b.Group(1_a), b.Binding(0_a));
-    }
-
-    void SetupUniformBuffer(VectorRef<const ast::StructMember*> members) {
-        ProgramBuilder& b = *this;
-        auto* s = b.Structure("Data", members);
-
-        b.GlobalVar("data", b.ty.Of(s), core::AddressSpace::kUniform, core::Access::kUndefined,
-                    b.Group(1_a), b.Binding(1_a));
-    }
-
-    void SetupFunction(VectorRef<const ast::Statement*> statements) {
-        ProgramBuilder& b = *this;
-        Vector attrs{
-            b.Stage(ast::PipelineStage::kFragment),
-        };
-        b.Func("main", tint::Empty, b.ty.void_(), std::move(statements), std::move(attrs));
-    }
-};
-
-using HlslASTPrinterTest_MemberAccessor = HlslASTPrinterTest_MemberAccessorBase<TestHelper>;
-
-template <typename T>
-using HlslASTPrinterTest_MemberAccessorWithParam =
-    HlslASTPrinterTest_MemberAccessorBase<TestParamHelper<T>>;
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, EmitExpression_MemberAccessor) {
-    auto* s = Structure("Data", Vector{Member("mem", ty.f32())});
-    GlobalVar("str", ty.Of(s), core::AddressSpace::kPrivate);
-
-    auto* expr = MemberAccessor("str", "mem");
-    WrapInFunction(Var("expr", ty.f32(), expr));
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(struct Data {
-  float mem;
-};
-
-static Data str = (Data)0;
-
-[numthreads(1, 1, 1)]
-void test_function() {
-  float expr = str.mem;
-  return;
-}
-)");
-}
-
-struct TypeCase {
-    create_type_func_ptr member_type;
-    std::string expected;
-};
-inline std::ostream& operator<<(std::ostream& out, TypeCase c) {
-    return out << c.expected;
-}
-
-using HlslASTPrinterTest_MemberAccessor_StorageBufferLoad_ConstantOffset =
-    HlslASTPrinterTest_MemberAccessorWithParam<TypeCase>;
-
-TEST_P(HlslASTPrinterTest_MemberAccessor_StorageBufferLoad_ConstantOffset, Test) {
-    // struct Data {
-    //   a : i32,
-    //   b : <type>,
-    // };
-    // var<storage> data : Data;
-    // data.b;
-
-    auto p = GetParam();
-
-    Enable(wgsl::Extension::kF16);
-
-    SetupStorageBuffer(Vector{
-        Member("a", ty.i32()),
-        Member("b", p.member_type(ty)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", MemberAccessor("data", "b"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(p.expected));
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest_MemberAccessor,
-    HlslASTPrinterTest_MemberAccessor_StorageBufferLoad_ConstantOffset,
-    testing::Values(TypeCase{ty_u32, "data.Load(4u)"},
-                    TypeCase{ty_f32, "asfloat(data.Load(4u))"},
-                    TypeCase{ty_i32, "asint(data.Load(4u))"},
-                    TypeCase{ty_f16, "data.Load<float16_t>(4u)"},
-                    TypeCase{ty_vec2<u32>, "data.Load2(8u)"},
-                    TypeCase{ty_vec2<f32>, "asfloat(data.Load2(8u))"},
-                    TypeCase{ty_vec2<i32>, "asint(data.Load2(8u))"},
-                    TypeCase{ty_vec2<f16>, "data.Load<vector<float16_t, 2> >(4u)"},
-                    TypeCase{ty_vec3<u32>, "data.Load3(16u)"},
-                    TypeCase{ty_vec3<f32>, "asfloat(data.Load3(16u))"},
-                    TypeCase{ty_vec3<i32>, "asint(data.Load3(16u))"},
-                    TypeCase{ty_vec3<f16>, "data.Load<vector<float16_t, 3> >(8u)"},
-                    TypeCase{ty_vec4<u32>, "data.Load4(16u)"},
-                    TypeCase{ty_vec4<f32>, "asfloat(data.Load4(16u))"},
-                    TypeCase{ty_vec4<i32>, "asint(data.Load4(16u))"},
-                    TypeCase{ty_vec4<f16>, "data.Load<vector<float16_t, 4> >(8u)"},
-                    TypeCase{ty_mat2x2<f32>,
-                             "return float2x2(asfloat(data.Load2((offset + 0u))), "
-                             "asfloat(data.Load2((offset + 8u))));"},
-                    TypeCase{ty_mat2x3<f32>,
-                             "return float2x3(asfloat(data.Load3((offset + 0u))), "
-                             "asfloat(data.Load3((offset + 16u))));"},
-                    TypeCase{ty_mat2x4<f32>,
-                             "return float2x4(asfloat(data.Load4((offset + 0u))), "
-                             "asfloat(data.Load4((offset + 16u))));"},
-                    TypeCase{ty_mat3x2<f32>,
-                             "return float3x2(asfloat(data.Load2((offset + 0u))), "
-                             "asfloat(data.Load2((offset + 8u))), "
-                             "asfloat(data.Load2((offset + 16u))));"},
-                    TypeCase{ty_mat3x3<f32>,
-                             "return float3x3(asfloat(data.Load3((offset + 0u))), "
-                             "asfloat(data.Load3((offset + 16u))), "
-                             "asfloat(data.Load3((offset + 32u))));"},
-                    TypeCase{ty_mat3x4<f32>,
-                             "return float3x4(asfloat(data.Load4((offset + 0u))), "
-                             "asfloat(data.Load4((offset + 16u))), "
-                             "asfloat(data.Load4((offset + 32u))));"},
-                    TypeCase{ty_mat4x2<f32>,
-                             "return float4x2(asfloat(data.Load2((offset + 0u))), "
-                             "asfloat(data.Load2((offset + 8u))), "
-                             "asfloat(data.Load2((offset + 16u))), "
-                             "asfloat(data.Load2((offset + 24u))));"},
-                    TypeCase{ty_mat4x3<f32>,
-                             "return float4x3(asfloat(data.Load3((offset + 0u))), "
-                             "asfloat(data.Load3((offset + 16u))), "
-                             "asfloat(data.Load3((offset + 32u))), "
-                             "asfloat(data.Load3((offset + 48u))));"},
-                    TypeCase{ty_mat4x4<f32>,
-                             "return float4x4(asfloat(data.Load4((offset + 0u))), "
-                             "asfloat(data.Load4((offset + 16u))), "
-                             "asfloat(data.Load4((offset + 32u))), "
-                             "asfloat(data.Load4((offset + 48u))));"},
-                    TypeCase{ty_mat2x2<f16>,
-                             "return matrix<float16_t, 2, 2>("
-                             "data.Load<vector<float16_t, 2> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 2> >((offset + 4u)));"},
-                    TypeCase{ty_mat2x3<f16>,
-                             "return matrix<float16_t, 2, 3>("
-                             "data.Load<vector<float16_t, 3> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 3> >((offset + 8u)));"},
-                    TypeCase{ty_mat2x4<f16>,
-                             "return matrix<float16_t, 2, 4>("
-                             "data.Load<vector<float16_t, 4> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 4> >((offset + 8u)));"},
-                    TypeCase{ty_mat3x2<f16>,
-                             "return matrix<float16_t, 3, 2>("
-                             "data.Load<vector<float16_t, 2> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 2> >((offset + 4u)), "
-                             "data.Load<vector<float16_t, 2> >((offset + 8u)));"},
-                    TypeCase{ty_mat3x3<f16>,
-                             "return matrix<float16_t, 3, 3>("
-                             "data.Load<vector<float16_t, 3> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 3> >((offset + 8u)), "
-                             "data.Load<vector<float16_t, 3> >((offset + 16u)));"},
-                    TypeCase{ty_mat3x4<f16>,
-                             "return matrix<float16_t, 3, 4>("
-                             "data.Load<vector<float16_t, 4> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 4> >((offset + 8u)), "
-                             "data.Load<vector<float16_t, 4> >((offset + 16u)));"},
-                    TypeCase{ty_mat4x2<f16>,
-                             "return matrix<float16_t, 4, 2>("
-                             "data.Load<vector<float16_t, 2> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 2> >((offset + 4u)), "
-                             "data.Load<vector<float16_t, 2> >((offset + 8u)), "
-                             "data.Load<vector<float16_t, 2> >((offset + 12u)));"},
-                    TypeCase{ty_mat4x3<f16>,
-                             "return matrix<float16_t, 4, 3>("
-                             "data.Load<vector<float16_t, 3> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 3> >((offset + 8u)), "
-                             "data.Load<vector<float16_t, 3> >((offset + 16u)), "
-                             "data.Load<vector<float16_t, 3> >((offset + 24u)));"},
-                    TypeCase{ty_mat4x4<f16>,
-                             "return matrix<float16_t, 4, 4>("
-                             "data.Load<vector<float16_t, 4> >((offset + 0u)), "
-                             "data.Load<vector<float16_t, 4> >((offset + 8u)), "
-                             "data.Load<vector<float16_t, 4> >((offset + 16u)), "
-                             "data.Load<vector<float16_t, 4> >((offset + 24u)));"}));
-
-using HlslASTPrinterTest_MemberAccessor_StorageBufferLoad_DynamicOffset =
-    HlslASTPrinterTest_MemberAccessorWithParam<TypeCase>;
-
-TEST_P(HlslASTPrinterTest_MemberAccessor_StorageBufferLoad_DynamicOffset, Test) {
-    // struct Inner {
-    //   a : i32,
-    //   b : <type>,
-    //   c : vec4<i32>,
-    // };
-    // struct Data {
-    //  arr : array<Inner, 4i>,
-    // }
-    // var<storage> data : Data;
-    // data.arr[i].b;
-
-    auto p = GetParam();
-
-    Enable(wgsl::Extension::kF16);
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.i32()),
-                                         Member("b", p.member_type(ty)),
-                                         Member("c", ty.vec4(ty.i32())),
-                                     });
-
-    SetupStorageBuffer(Vector{
-        Member("arr", ty.array(ty.Of(inner), 4_i)),
-    });
-
-    auto* i = Var("i", Expr(2_i));
-
-    SetupFunction(Vector{
-        Decl(i),
-        Decl(Var("x", MemberAccessor(IndexAccessor(MemberAccessor("data", "arr"), i), "b"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(p.expected));
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest_MemberAccessor,
-    HlslASTPrinterTest_MemberAccessor_StorageBufferLoad_DynamicOffset,
-    testing::Values(
-        TypeCase{ty_u32, "data.Load(((32u * uint(i)) + 4u))"},
-        TypeCase{ty_f32, "asfloat(data.Load(((32u * uint(i)) + 4u)))"},
-        TypeCase{ty_i32, "asint(data.Load(((32u * uint(i)) + 4u)))"},
-        TypeCase{ty_f16, "data.Load<float16_t>(((32u * uint(i)) + 4u))"},
-        TypeCase{ty_vec2<u32>, "data.Load2(((32u * uint(i)) + 8u))"},
-        TypeCase{ty_vec2<f32>, "asfloat(data.Load2(((32u * uint(i)) + 8u)))"},
-        TypeCase{ty_vec2<i32>, "asint(data.Load2(((32u * uint(i)) + 8u)))"},
-        TypeCase{ty_vec2<f16>, "data.Load<vector<float16_t, 2> >(((32u * uint(i)) + 4u))"},
-        TypeCase{ty_vec3<u32>, "data.Load3(((48u * uint(i)) + 16u))"},
-        TypeCase{ty_vec3<f32>, "asfloat(data.Load3(((48u * uint(i)) + 16u)))"},
-        TypeCase{ty_vec3<i32>, "asint(data.Load3(((48u * uint(i)) + 16u)))"},
-        TypeCase{ty_vec3<f16>, "data.Load<vector<float16_t, 3> >(((32u * uint(i)) + 8u))"},
-        TypeCase{ty_vec4<u32>, "data.Load4(((48u * uint(i)) + 16u))"},
-        TypeCase{ty_vec4<f32>, "asfloat(data.Load4(((48u * uint(i)) + 16u)))"},
-        TypeCase{ty_vec4<i32>, "asint(data.Load4(((48u * uint(i)) + 16u)))"},
-        TypeCase{ty_vec4<f16>, "data.Load<vector<float16_t, 4> >(((32u * uint(i)) + 8u))"},
-        TypeCase{ty_mat2x2<f32>,
-                 "return float2x2(asfloat(data.Load2((offset + 0u))), "
-                 "asfloat(data.Load2((offset + 8u))));"},
-        TypeCase{ty_mat2x3<f32>,
-                 "return float2x3(asfloat(data.Load3((offset + 0u))), "
-                 "asfloat(data.Load3((offset + 16u))));"},
-        TypeCase{ty_mat2x4<f32>,
-                 "return float2x4(asfloat(data.Load4((offset + 0u))), "
-                 "asfloat(data.Load4((offset + 16u))));"},
-        TypeCase{ty_mat3x2<f32>,
-                 "return float3x2(asfloat(data.Load2((offset + 0u))), "
-                 "asfloat(data.Load2((offset + 8u))), "
-                 "asfloat(data.Load2((offset + 16u))));"},
-        TypeCase{ty_mat3x3<f32>,
-                 "return float3x3(asfloat(data.Load3((offset + 0u))), "
-                 "asfloat(data.Load3((offset + 16u))), "
-                 "asfloat(data.Load3((offset + 32u))));"},
-        TypeCase{ty_mat3x4<f32>,
-                 "return float3x4(asfloat(data.Load4((offset + 0u))), "
-                 "asfloat(data.Load4((offset + 16u))), "
-                 "asfloat(data.Load4((offset + 32u))));"},
-        TypeCase{ty_mat4x2<f32>,
-                 "return float4x2(asfloat(data.Load2((offset + 0u))), "
-                 "asfloat(data.Load2((offset + 8u))), "
-                 "asfloat(data.Load2((offset + 16u))), "
-                 "asfloat(data.Load2((offset + 24u))));"},
-        TypeCase{ty_mat4x3<f32>,
-                 "return float4x3(asfloat(data.Load3((offset + 0u))), "
-                 "asfloat(data.Load3((offset + 16u))), "
-                 "asfloat(data.Load3((offset + 32u))), "
-                 "asfloat(data.Load3((offset + 48u))));"},
-        TypeCase{ty_mat4x4<f32>,
-                 "return float4x4(asfloat(data.Load4((offset + 0u))), "
-                 "asfloat(data.Load4((offset + 16u))), "
-                 "asfloat(data.Load4((offset + 32u))), "
-                 "asfloat(data.Load4((offset + 48u))));"},
-        TypeCase{ty_mat2x2<f16>,
-                 "return matrix<float16_t, 2, 2>("
-                 "data.Load<vector<float16_t, 2> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 2> >((offset + 4u)));"},
-        TypeCase{ty_mat2x3<f16>,
-                 "return matrix<float16_t, 2, 3>("
-                 "data.Load<vector<float16_t, 3> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 3> >((offset + 8u)));"},
-        TypeCase{ty_mat2x4<f16>,
-                 "return matrix<float16_t, 2, 4>("
-                 "data.Load<vector<float16_t, 4> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 4> >((offset + 8u)));"},
-        TypeCase{ty_mat3x2<f16>,
-                 "return matrix<float16_t, 3, 2>("
-                 "data.Load<vector<float16_t, 2> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 2> >((offset + 4u)), "
-                 "data.Load<vector<float16_t, 2> >((offset + 8u)));"},
-        TypeCase{ty_mat3x3<f16>,
-                 "return matrix<float16_t, 3, 3>("
-                 "data.Load<vector<float16_t, 3> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 3> >((offset + 8u)), "
-                 "data.Load<vector<float16_t, 3> >((offset + 16u)));"},
-        TypeCase{ty_mat3x4<f16>,
-                 "return matrix<float16_t, 3, 4>("
-                 "data.Load<vector<float16_t, 4> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 4> >((offset + 8u)), "
-                 "data.Load<vector<float16_t, 4> >((offset + 16u)));"},
-        TypeCase{ty_mat4x2<f16>,
-                 "return matrix<float16_t, 4, 2>("
-                 "data.Load<vector<float16_t, 2> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 2> >((offset + 4u)), "
-                 "data.Load<vector<float16_t, 2> >((offset + 8u)), "
-                 "data.Load<vector<float16_t, 2> >((offset + 12u)));"},
-        TypeCase{ty_mat4x3<f16>,
-                 "return matrix<float16_t, 4, 3>("
-                 "data.Load<vector<float16_t, 3> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 3> >((offset + 8u)), "
-                 "data.Load<vector<float16_t, 3> >((offset + 16u)), "
-                 "data.Load<vector<float16_t, 3> >((offset + 24u)));"},
-        TypeCase{ty_mat4x4<f16>,
-                 "return matrix<float16_t, 4, 4>("
-                 "data.Load<vector<float16_t, 4> >((offset + 0u)), "
-                 "data.Load<vector<float16_t, 4> >((offset + 8u)), "
-                 "data.Load<vector<float16_t, 4> >((offset + 16u)), "
-                 "data.Load<vector<float16_t, 4> >((offset + 24u)));"}));
-
-using HlslASTPrinterTest_MemberAccessor_UniformBufferLoad_ConstantOffset =
-    HlslASTPrinterTest_MemberAccessorWithParam<TypeCase>;
-TEST_P(HlslASTPrinterTest_MemberAccessor_UniformBufferLoad_ConstantOffset, Test) {
-    // struct Data {
-    //   a : i32,
-    //   b : <type>,
-    // };
-    // var<uniform> data : Data;
-    // data.b;
-
-    auto p = GetParam();
-
-    Enable(wgsl::Extension::kF16);
-
-    SetupUniformBuffer(Vector{
-        Member("a", ty.i32()),
-        Member("b", p.member_type(ty)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", MemberAccessor("data", "b"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(p.expected));
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest_MemberAccessor,
-    HlslASTPrinterTest_MemberAccessor_UniformBufferLoad_ConstantOffset,
-    testing::Values(TypeCase{ty_u32, "uint x = data[0].y;"},
-                    TypeCase{ty_f32, "float x = asfloat(data[0].y);"},
-                    TypeCase{ty_i32, "int x = asint(data[0].y);"},
-                    TypeCase{ty_f16, "float16_t x = float16_t(f16tof32(((data[0].y) & 0xFFFF)));"},
-                    TypeCase{ty_vec2<u32>, "uint2 x = data[0].zw;"},
-                    TypeCase{ty_vec2<f32>, "float2 x = asfloat(data[0].zw);"},
-                    TypeCase{ty_vec2<i32>, "int2 x = asint(data[0].zw);"},
-                    TypeCase{ty_vec2<f16>, R"(uint ubo_load = data[0].y;
-  vector<float16_t, 2> x = vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16)));)"},
-                    TypeCase{ty_vec3<u32>, "uint3 x = data[1].xyz;"},
-                    TypeCase{ty_vec3<f32>, "float3 x = asfloat(data[1].xyz);"},
-                    TypeCase{ty_vec3<i32>, "int3 x = asint(data[1].xyz);"},
-                    TypeCase{ty_vec3<f16>, R"(uint2 ubo_load = data[0].zw;
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-  vector<float16_t, 3> x = vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]);)"},
-                    TypeCase{ty_vec4<u32>, "uint4 x = data[1];"},
-                    TypeCase{ty_vec4<f32>, "float4 x = asfloat(data[1]);"},
-                    TypeCase{ty_vec4<i32>, "int4 x = asint(data[1]);"},
-                    TypeCase{ty_vec4<f16>,
-                             R"(uint2 ubo_load = data[0].zw;
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
-  vector<float16_t, 4> x = vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]);)"},
-                    TypeCase{ty_mat2x2<f32>, R"(float2x2 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load = data[scalar_offset / 4];
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset_1 / 4];
-  return float2x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)));
-})"},
-                    TypeCase{ty_mat2x3<f32>, R"(float2x3 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  return float2x3(asfloat(data[scalar_offset / 4].xyz), asfloat(data[scalar_offset_1 / 4].xyz));
-})"},
-                    TypeCase{ty_mat2x4<f32>, R"(float2x4 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  return float2x4(asfloat(data[scalar_offset / 4]), asfloat(data[scalar_offset_1 / 4]));
-})"},
-                    TypeCase{ty_mat3x2<f32>, R"(float3x2 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load = data[scalar_offset / 4];
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset_1 / 4];
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_2 = data[scalar_offset_2 / 4];
-  return float3x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)), asfloat(((scalar_offset_2 & 2) ? ubo_load_2.zw : ubo_load_2.xy)));
-})"},
-                    TypeCase{ty_mat3x3<f32>, R"(float3x3 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  const uint scalar_offset_2 = ((offset + 32u)) / 4;
-  return float3x3(asfloat(data[scalar_offset / 4].xyz), asfloat(data[scalar_offset_1 / 4].xyz), asfloat(data[scalar_offset_2 / 4].xyz));
-})"},
-                    TypeCase{ty_mat3x4<f32>, R"(float3x4 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  const uint scalar_offset_2 = ((offset + 32u)) / 4;
-  return float3x4(asfloat(data[scalar_offset / 4]), asfloat(data[scalar_offset_1 / 4]), asfloat(data[scalar_offset_2 / 4]));
-})"},
-                    TypeCase{ty_mat4x2<f32>, R"(float4x2 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load = data[scalar_offset / 4];
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset_1 / 4];
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_2 = data[scalar_offset_2 / 4];
-  const uint scalar_offset_3 = ((offset + 24u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_3 / 4];
-  return float4x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)), asfloat(((scalar_offset_2 & 2) ? ubo_load_2.zw : ubo_load_2.xy)), asfloat(((scalar_offset_3 & 2) ? ubo_load_3.zw : ubo_load_3.xy)));
-})"},
-                    TypeCase{ty_mat4x3<f32>, R"(float4x3 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  const uint scalar_offset_2 = ((offset + 32u)) / 4;
-  const uint scalar_offset_3 = ((offset + 48u)) / 4;
-  return float4x3(asfloat(data[scalar_offset / 4].xyz), asfloat(data[scalar_offset_1 / 4].xyz), asfloat(data[scalar_offset_2 / 4].xyz), asfloat(data[scalar_offset_3 / 4].xyz));
-})"},
-                    TypeCase{ty_mat4x4<f32>, R"(float4x4 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  const uint scalar_offset_2 = ((offset + 32u)) / 4;
-  const uint scalar_offset_3 = ((offset + 48u)) / 4;
-  return float4x4(asfloat(data[scalar_offset / 4]), asfloat(data[scalar_offset_1 / 4]), asfloat(data[scalar_offset_2 / 4]), asfloat(data[scalar_offset_3 / 4]));
-})"},
-                    TypeCase{ty_mat2x2<f16>,
-                             R"(matrix<float16_t, 2, 2> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint ubo_load = data[scalar_offset / 4][scalar_offset % 4];
-  const uint scalar_offset_1 = ((offset + 4u)) / 4;
-  uint ubo_load_1 = data[scalar_offset_1 / 4][scalar_offset_1 % 4];
-  return matrix<float16_t, 2, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))));
-})"},
-                    TypeCase{ty_mat2x3<f16>,
-                             R"(matrix<float16_t, 2, 3> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
-  return matrix<float16_t, 2, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]));
-})"},
-                    TypeCase{ty_mat2x4<f16>,
-                             R"(matrix<float16_t, 2, 4> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
-  return matrix<float16_t, 2, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]));
-})"},
-                    TypeCase{ty_mat3x2<f16>,
-                             R"(matrix<float16_t, 3, 2> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint ubo_load = data[scalar_offset / 4][scalar_offset % 4];
-  const uint scalar_offset_1 = ((offset + 4u)) / 4;
-  uint ubo_load_1 = data[scalar_offset_1 / 4][scalar_offset_1 % 4];
-  const uint scalar_offset_2 = ((offset + 8u)) / 4;
-  uint ubo_load_2 = data[scalar_offset_2 / 4][scalar_offset_2 % 4];
-  return matrix<float16_t, 3, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_2 & 0xFFFF)), float16_t(f16tof32(ubo_load_2 >> 16))));
-})"},
-                    TypeCase{ty_mat3x3<f16>,
-                             R"(matrix<float16_t, 3, 3> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_5 = data[scalar_offset_2 / 4];
-  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
-  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
-  float16_t ubo_load_4_y = f16tof32(ubo_load_4[0] >> 16);
-  return matrix<float16_t, 3, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]), vector<float16_t, 3>(ubo_load_4_xz[0], ubo_load_4_y, ubo_load_4_xz[1]));
-})"},
-                    TypeCase{ty_mat3x4<f16>,
-                             R"(matrix<float16_t, 3, 4> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_5 = data[scalar_offset_2 / 4];
-  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
-  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_4_yw = vector<float16_t, 2>(f16tof32(ubo_load_4 >> 16));
-  return matrix<float16_t, 3, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]), vector<float16_t, 4>(ubo_load_4_xz[0], ubo_load_4_yw[0], ubo_load_4_xz[1], ubo_load_4_yw[1]));)"},
-                    TypeCase{ty_mat4x2<f16>,
-                             R"(matrix<float16_t, 4, 2> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint ubo_load = data[scalar_offset / 4][scalar_offset % 4];
-  const uint scalar_offset_1 = ((offset + 4u)) / 4;
-  uint ubo_load_1 = data[scalar_offset_1 / 4][scalar_offset_1 % 4];
-  const uint scalar_offset_2 = ((offset + 8u)) / 4;
-  uint ubo_load_2 = data[scalar_offset_2 / 4][scalar_offset_2 % 4];
-  const uint scalar_offset_3 = ((offset + 12u)) / 4;
-  uint ubo_load_3 = data[scalar_offset_3 / 4][scalar_offset_3 % 4];
-  return matrix<float16_t, 4, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_2 & 0xFFFF)), float16_t(f16tof32(ubo_load_2 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_3 & 0xFFFF)), float16_t(f16tof32(ubo_load_3 >> 16))));
-})"},
-                    TypeCase{ty_mat4x3<f16>,
-                             R"(matrix<float16_t, 4, 3> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_5 = data[scalar_offset_2 / 4];
-  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
-  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
-  float16_t ubo_load_4_y = f16tof32(ubo_load_4[0] >> 16);
-  const uint scalar_offset_3 = ((offset + 24u)) / 4;
-  uint4 ubo_load_7 = data[scalar_offset_3 / 4];
-  uint2 ubo_load_6 = ((scalar_offset_3 & 2) ? ubo_load_7.zw : ubo_load_7.xy);
-  vector<float16_t, 2> ubo_load_6_xz = vector<float16_t, 2>(f16tof32(ubo_load_6 & 0xFFFF));
-  float16_t ubo_load_6_y = f16tof32(ubo_load_6[0] >> 16);
-  return matrix<float16_t, 4, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]), vector<float16_t, 3>(ubo_load_4_xz[0], ubo_load_4_y, ubo_load_4_xz[1]), vector<float16_t, 3>(ubo_load_6_xz[0], ubo_load_6_y, ubo_load_6_xz[1]));
-})"},
-                    TypeCase{ty_mat4x4<f16>,
-                             R"(matrix<float16_t, 4, 4> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_5 = data[scalar_offset_2 / 4];
-  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
-  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_4_yw = vector<float16_t, 2>(f16tof32(ubo_load_4 >> 16));
-  const uint scalar_offset_3 = ((offset + 24u)) / 4;
-  uint4 ubo_load_7 = data[scalar_offset_3 / 4];
-  uint2 ubo_load_6 = ((scalar_offset_3 & 2) ? ubo_load_7.zw : ubo_load_7.xy);
-  vector<float16_t, 2> ubo_load_6_xz = vector<float16_t, 2>(f16tof32(ubo_load_6 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_6_yw = vector<float16_t, 2>(f16tof32(ubo_load_6 >> 16));
-  return matrix<float16_t, 4, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]), vector<float16_t, 4>(ubo_load_4_xz[0], ubo_load_4_yw[0], ubo_load_4_xz[1], ubo_load_4_yw[1]), vector<float16_t, 4>(ubo_load_6_xz[0], ubo_load_6_yw[0], ubo_load_6_xz[1], ubo_load_6_yw[1]));
-})"}));
-
-using HlslASTPrinterTest_MemberAccessor_UniformBufferLoad_DynamicOffset =
-    HlslASTPrinterTest_MemberAccessorWithParam<TypeCase>;
-
-TEST_P(HlslASTPrinterTest_MemberAccessor_UniformBufferLoad_DynamicOffset, Test) {
-    // struct Inner {
-    //   a : i32,
-    //   b : <type>,
-    //   c : vec4<i32>,
-    // };
-    // struct Data {
-    //  arr : array<Inner, 4i>,
-    // }
-    // var<uniform> data : Data;
-    // data.arr[i].b;
-
-    auto p = GetParam();
-
-    Enable(wgsl::Extension::kF16);
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.i32()),
-                                         Member("b", p.member_type(ty)),
-                                         Member("c", ty.vec4(ty.i32())),
-                                     });
-
-    SetupUniformBuffer(Vector{
-        Member("arr", ty.array(ty.Of(inner), 4_i)),
-    });
-
-    auto* i = Var("i", Expr(2_i));
-
-    SetupFunction(Vector{
-        Decl(i),
-        Decl(Var("x", MemberAccessor(IndexAccessor(MemberAccessor("data", "arr"), i), "b"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(p.expected));
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest_MemberAccessor,
-    HlslASTPrinterTest_MemberAccessor_UniformBufferLoad_DynamicOffset,
-    testing::Values(
-        TypeCase{ty_u32, "x = data[scalar_offset / 4][scalar_offset % 4]"},
-        TypeCase{ty_f32, "x = asfloat(data[scalar_offset / 4][scalar_offset % 4])"},
-        TypeCase{ty_i32, "x = asint(data[scalar_offset / 4][scalar_offset % 4])"},
-        TypeCase{ty_f16, R"(const uint scalar_offset_bytes = (((32u * uint(i)) + 4u));
-  const uint scalar_offset_index = scalar_offset_bytes / 4;
-  float16_t x = float16_t(f16tof32(((data[scalar_offset_index / 4][scalar_offset_index % 4] >> (scalar_offset_bytes % 4 == 0 ? 0 : 16)) & 0xFFFF)));)"},
-        TypeCase{ty_vec2<u32>, R"(uint4 ubo_load = data[scalar_offset / 4];
-  uint2 x = ((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy);)"},
-        TypeCase{ty_vec2<f32>, R"(uint4 ubo_load = data[scalar_offset / 4];
-  float2 x = asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy));)"},
-        TypeCase{ty_vec2<i32>, R"(uint4 ubo_load = data[scalar_offset / 4];
-  int2 x = asint(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy));)"},
-        TypeCase{ty_vec2<f16>, R"(const uint scalar_offset = (((32u * uint(i)) + 4u)) / 4;
-  uint ubo_load = data[scalar_offset / 4][scalar_offset % 4];
-  vector<float16_t, 2> x = vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16)));)"},
-        TypeCase{ty_vec3<u32>, "x = data[scalar_offset / 4].xyz"},
-        TypeCase{ty_vec3<f32>, "x = asfloat(data[scalar_offset / 4].xyz)"},
-        TypeCase{ty_vec3<i32>, "x = asint(data[scalar_offset / 4].xyz)"},
-        TypeCase{ty_vec3<f16>, R"(const uint scalar_offset = (((32u * uint(i)) + 8u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-  vector<float16_t, 3> x = vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]);)"},
-        TypeCase{ty_vec4<u32>, "x = data[scalar_offset / 4]"},
-        TypeCase{ty_vec4<f32>, "x = asfloat(data[scalar_offset / 4])"},
-        TypeCase{ty_vec4<i32>, "x = asint(data[scalar_offset / 4])"},
-        TypeCase{ty_vec4<f16>, R"(const uint scalar_offset = (((32u * uint(i)) + 8u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
-  vector<float16_t, 4> x = vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]);)"},
-        TypeCase{ty_mat2x2<f32>, R"(float2x2 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load = data[scalar_offset / 4];
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset_1 / 4];
-  return float2x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)));
-})"},
-        TypeCase{ty_mat2x3<f32>, R"(float2x3 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  return float2x3(asfloat(data[scalar_offset / 4].xyz), asfloat(data[scalar_offset_1 / 4].xyz));
-})"},
-        TypeCase{ty_mat2x4<f32>, R"(float2x4 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  return float2x4(asfloat(data[scalar_offset / 4]), asfloat(data[scalar_offset_1 / 4]));
-})"},
-        TypeCase{ty_mat3x2<f32>, R"(float3x2 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load = data[scalar_offset / 4];
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset_1 / 4];
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_2 = data[scalar_offset_2 / 4];
-  return float3x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)), asfloat(((scalar_offset_2 & 2) ? ubo_load_2.zw : ubo_load_2.xy)));
-})"},
-        TypeCase{ty_mat3x3<f32>, R"(float3x3 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  const uint scalar_offset_2 = ((offset + 32u)) / 4;
-  return float3x3(asfloat(data[scalar_offset / 4].xyz), asfloat(data[scalar_offset_1 / 4].xyz), asfloat(data[scalar_offset_2 / 4].xyz));
-})"},
-        TypeCase{ty_mat3x4<f32>, R"(float3x4 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  const uint scalar_offset_2 = ((offset + 32u)) / 4;
-  return float3x4(asfloat(data[scalar_offset / 4]), asfloat(data[scalar_offset_1 / 4]), asfloat(data[scalar_offset_2 / 4]));
-})"},
-        TypeCase{ty_mat4x2<f32>, R"(float4x2 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load = data[scalar_offset / 4];
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset_1 / 4];
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_2 = data[scalar_offset_2 / 4];
-  const uint scalar_offset_3 = ((offset + 24u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_3 / 4];
-  return float4x2(asfloat(((scalar_offset & 2) ? ubo_load.zw : ubo_load.xy)), asfloat(((scalar_offset_1 & 2) ? ubo_load_1.zw : ubo_load_1.xy)), asfloat(((scalar_offset_2 & 2) ? ubo_load_2.zw : ubo_load_2.xy)), asfloat(((scalar_offset_3 & 2) ? ubo_load_3.zw : ubo_load_3.xy)));
-})"},
-        TypeCase{ty_mat4x3<f32>, R"(float4x3 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  const uint scalar_offset_2 = ((offset + 32u)) / 4;
-  const uint scalar_offset_3 = ((offset + 48u)) / 4;
-  return float4x3(asfloat(data[scalar_offset / 4].xyz), asfloat(data[scalar_offset_1 / 4].xyz), asfloat(data[scalar_offset_2 / 4].xyz), asfloat(data[scalar_offset_3 / 4].xyz));
-})"},
-        TypeCase{ty_mat4x4<f32>, R"(float4x4 data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  const uint scalar_offset_1 = ((offset + 16u)) / 4;
-  const uint scalar_offset_2 = ((offset + 32u)) / 4;
-  const uint scalar_offset_3 = ((offset + 48u)) / 4;
-  return float4x4(asfloat(data[scalar_offset / 4]), asfloat(data[scalar_offset_1 / 4]), asfloat(data[scalar_offset_2 / 4]), asfloat(data[scalar_offset_3 / 4]));
-})"},
-        TypeCase{ty_mat2x2<f16>,
-                 R"(matrix<float16_t, 2, 2> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint ubo_load = data[scalar_offset / 4][scalar_offset % 4];
-  const uint scalar_offset_1 = ((offset + 4u)) / 4;
-  uint ubo_load_1 = data[scalar_offset_1 / 4][scalar_offset_1 % 4];
-  return matrix<float16_t, 2, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))));
-})"},
-        TypeCase{ty_mat2x3<f16>,
-                 R"(matrix<float16_t, 2, 3> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
-  return matrix<float16_t, 2, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]));
-})"},
-        TypeCase{ty_mat2x4<f16>,
-                 R"(matrix<float16_t, 2, 4> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
-  return matrix<float16_t, 2, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]));
-})"},
-        TypeCase{ty_mat3x2<f16>,
-                 R"(matrix<float16_t, 3, 2> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint ubo_load = data[scalar_offset / 4][scalar_offset % 4];
-  const uint scalar_offset_1 = ((offset + 4u)) / 4;
-  uint ubo_load_1 = data[scalar_offset_1 / 4][scalar_offset_1 % 4];
-  const uint scalar_offset_2 = ((offset + 8u)) / 4;
-  uint ubo_load_2 = data[scalar_offset_2 / 4][scalar_offset_2 % 4];
-  return matrix<float16_t, 3, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_2 & 0xFFFF)), float16_t(f16tof32(ubo_load_2 >> 16))));
-})"},
-        TypeCase{ty_mat3x3<f16>,
-                 R"(matrix<float16_t, 3, 3> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_5 = data[scalar_offset_2 / 4];
-  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
-  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
-  float16_t ubo_load_4_y = f16tof32(ubo_load_4[0] >> 16);
-  return matrix<float16_t, 3, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]), vector<float16_t, 3>(ubo_load_4_xz[0], ubo_load_4_y, ubo_load_4_xz[1]));
-})"},
-        TypeCase{ty_mat3x4<f16>,
-                 R"(matrix<float16_t, 3, 4> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_5 = data[scalar_offset_2 / 4];
-  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
-  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_4_yw = vector<float16_t, 2>(f16tof32(ubo_load_4 >> 16));
-  return matrix<float16_t, 3, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]), vector<float16_t, 4>(ubo_load_4_xz[0], ubo_load_4_yw[0], ubo_load_4_xz[1], ubo_load_4_yw[1]));
-})"},
-        TypeCase{ty_mat4x2<f16>,
-                 R"(matrix<float16_t, 4, 2> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint ubo_load = data[scalar_offset / 4][scalar_offset % 4];
-  const uint scalar_offset_1 = ((offset + 4u)) / 4;
-  uint ubo_load_1 = data[scalar_offset_1 / 4][scalar_offset_1 % 4];
-  const uint scalar_offset_2 = ((offset + 8u)) / 4;
-  uint ubo_load_2 = data[scalar_offset_2 / 4][scalar_offset_2 % 4];
-  const uint scalar_offset_3 = ((offset + 12u)) / 4;
-  uint ubo_load_3 = data[scalar_offset_3 / 4][scalar_offset_3 % 4];
-  return matrix<float16_t, 4, 2>(vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)), float16_t(f16tof32(ubo_load >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_1 & 0xFFFF)), float16_t(f16tof32(ubo_load_1 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_2 & 0xFFFF)), float16_t(f16tof32(ubo_load_2 >> 16))), vector<float16_t, 2>(float16_t(f16tof32(ubo_load_3 & 0xFFFF)), float16_t(f16tof32(ubo_load_3 >> 16))));
-})"},
-        TypeCase{ty_mat4x3<f16>,
-                 R"(matrix<float16_t, 4, 3> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  float16_t ubo_load_2_y = f16tof32(ubo_load_2[0] >> 16);
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_5 = data[scalar_offset_2 / 4];
-  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
-  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
-  float16_t ubo_load_4_y = f16tof32(ubo_load_4[0] >> 16);
-  const uint scalar_offset_3 = ((offset + 24u)) / 4;
-  uint4 ubo_load_7 = data[scalar_offset_3 / 4];
-  uint2 ubo_load_6 = ((scalar_offset_3 & 2) ? ubo_load_7.zw : ubo_load_7.xy);
-  vector<float16_t, 2> ubo_load_6_xz = vector<float16_t, 2>(f16tof32(ubo_load_6 & 0xFFFF));
-  float16_t ubo_load_6_y = f16tof32(ubo_load_6[0] >> 16);
-  return matrix<float16_t, 4, 3>(vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1]), vector<float16_t, 3>(ubo_load_2_xz[0], ubo_load_2_y, ubo_load_2_xz[1]), vector<float16_t, 3>(ubo_load_4_xz[0], ubo_load_4_y, ubo_load_4_xz[1]), vector<float16_t, 3>(ubo_load_6_xz[0], ubo_load_6_y, ubo_load_6_xz[1]));
-})"},
-        TypeCase{ty_mat4x4<f16>,
-                 R"(matrix<float16_t, 4, 4> data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  uint4 ubo_load_1 = data[scalar_offset / 4];
-  uint2 ubo_load = ((scalar_offset & 2) ? ubo_load_1.zw : ubo_load_1.xy);
-  vector<float16_t, 2> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load & 0xFFFF));
-  vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >> 16));
-  const uint scalar_offset_1 = ((offset + 8u)) / 4;
-  uint4 ubo_load_3 = data[scalar_offset_1 / 4];
-  uint2 ubo_load_2 = ((scalar_offset_1 & 2) ? ubo_load_3.zw : ubo_load_3.xy);
-  vector<float16_t, 2> ubo_load_2_xz = vector<float16_t, 2>(f16tof32(ubo_load_2 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_2_yw = vector<float16_t, 2>(f16tof32(ubo_load_2 >> 16));
-  const uint scalar_offset_2 = ((offset + 16u)) / 4;
-  uint4 ubo_load_5 = data[scalar_offset_2 / 4];
-  uint2 ubo_load_4 = ((scalar_offset_2 & 2) ? ubo_load_5.zw : ubo_load_5.xy);
-  vector<float16_t, 2> ubo_load_4_xz = vector<float16_t, 2>(f16tof32(ubo_load_4 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_4_yw = vector<float16_t, 2>(f16tof32(ubo_load_4 >> 16));
-  const uint scalar_offset_3 = ((offset + 24u)) / 4;
-  uint4 ubo_load_7 = data[scalar_offset_3 / 4];
-  uint2 ubo_load_6 = ((scalar_offset_3 & 2) ? ubo_load_7.zw : ubo_load_7.xy);
-  vector<float16_t, 2> ubo_load_6_xz = vector<float16_t, 2>(f16tof32(ubo_load_6 & 0xFFFF));
-  vector<float16_t, 2> ubo_load_6_yw = vector<float16_t, 2>(f16tof32(ubo_load_6 >> 16));
-  return matrix<float16_t, 4, 4>(vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1], ubo_load_yw[1]), vector<float16_t, 4>(ubo_load_2_xz[0], ubo_load_2_yw[0], ubo_load_2_xz[1], ubo_load_2_yw[1]), vector<float16_t, 4>(ubo_load_4_xz[0], ubo_load_4_yw[0], ubo_load_4_xz[1], ubo_load_4_yw[1]), vector<float16_t, 4>(ubo_load_6_xz[0], ubo_load_6_yw[0], ubo_load_6_xz[1], ubo_load_6_yw[1]));
-})"}));
-
-using HlslASTPrinterTest_MemberAccessor_StorageBufferStore =
-    HlslASTPrinterTest_MemberAccessorWithParam<TypeCase>;
-TEST_P(HlslASTPrinterTest_MemberAccessor_StorageBufferStore, Test) {
-    // struct Data {
-    //   a : i32,
-    //   b : <type>,
-    // };
-    // var<storage> data : Data;
-    // data.b = <type>();
-
-    auto p = GetParam();
-
-    Enable(wgsl::Extension::kF16);
-
-    SetupStorageBuffer(Vector{
-        Member("a", ty.i32()),
-        Member("b", p.member_type(ty)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("value", p.member_type(ty), Call(p.member_type(ty)))),
-        Assign(MemberAccessor("data", "b"), Expr("value")),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(p.expected));
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest_MemberAccessor,
-    HlslASTPrinterTest_MemberAccessor_StorageBufferStore,
-    testing::Values(TypeCase{ty_u32, "data.Store(4u, asuint(value))"},
-                    TypeCase{ty_f32, "data.Store(4u, asuint(value))"},
-                    TypeCase{ty_i32, "data.Store(4u, asuint(value))"},
-                    TypeCase{ty_f16, "data.Store<float16_t>(4u, value)"},
-                    TypeCase{ty_vec2<u32>, "data.Store2(8u, asuint(value))"},
-                    TypeCase{ty_vec2<f32>, "data.Store2(8u, asuint(value))"},
-                    TypeCase{ty_vec2<i32>, "data.Store2(8u, asuint(value))"},
-                    TypeCase{ty_vec2<f16>, "data.Store<vector<float16_t, 2> >(4u, value)"},
-                    TypeCase{ty_vec3<u32>, "data.Store3(16u, asuint(value))"},
-                    TypeCase{ty_vec3<f32>, "data.Store3(16u, asuint(value))"},
-                    TypeCase{ty_vec3<i32>, "data.Store3(16u, asuint(value))"},
-                    TypeCase{ty_vec3<f16>, "data.Store<vector<float16_t, 3> >(8u, value)"},
-                    TypeCase{ty_vec4<u32>, "data.Store4(16u, asuint(value))"},
-                    TypeCase{ty_vec4<f32>, "data.Store4(16u, asuint(value))"},
-                    TypeCase{ty_vec4<i32>, "data.Store4(16u, asuint(value))"},
-                    TypeCase{ty_vec4<f16>, "data.Store<vector<float16_t, 4> >(8u, value)"},
-                    TypeCase{ty_mat2x2<f32>, R"(
-
-void data_store(uint offset, float2x2 value) {
-  data.Store2((offset + 0u), asuint(value[0u]));
-  data.Store2((offset + 8u), asuint(value[1u]));
-})"},
-                    TypeCase{ty_mat2x3<f32>, R"({
-  data.Store3((offset + 0u), asuint(value[0u]));
-  data.Store3((offset + 16u), asuint(value[1u]));
-})"},
-                    TypeCase{ty_mat2x4<f32>, R"({
-  data.Store4((offset + 0u), asuint(value[0u]));
-  data.Store4((offset + 16u), asuint(value[1u]));
-})"},
-                    TypeCase{ty_mat3x2<f32>, R"({
-  data.Store2((offset + 0u), asuint(value[0u]));
-  data.Store2((offset + 8u), asuint(value[1u]));
-  data.Store2((offset + 16u), asuint(value[2u]));
-})"},
-                    TypeCase{ty_mat3x3<f32>, R"({
-  data.Store3((offset + 0u), asuint(value[0u]));
-  data.Store3((offset + 16u), asuint(value[1u]));
-  data.Store3((offset + 32u), asuint(value[2u]));
-})"},
-                    TypeCase{ty_mat3x4<f32>, R"({
-  data.Store4((offset + 0u), asuint(value[0u]));
-  data.Store4((offset + 16u), asuint(value[1u]));
-  data.Store4((offset + 32u), asuint(value[2u]));
-})"},
-                    TypeCase{ty_mat4x2<f32>, R"({
-  data.Store2((offset + 0u), asuint(value[0u]));
-  data.Store2((offset + 8u), asuint(value[1u]));
-  data.Store2((offset + 16u), asuint(value[2u]));
-  data.Store2((offset + 24u), asuint(value[3u]));
-})"},
-                    TypeCase{ty_mat4x3<f32>, R"({
-  data.Store3((offset + 0u), asuint(value[0u]));
-  data.Store3((offset + 16u), asuint(value[1u]));
-  data.Store3((offset + 32u), asuint(value[2u]));
-  data.Store3((offset + 48u), asuint(value[3u]));
-})"},
-                    TypeCase{ty_mat4x4<f32>, R"({
-  data.Store4((offset + 0u), asuint(value[0u]));
-  data.Store4((offset + 16u), asuint(value[1u]));
-  data.Store4((offset + 32u), asuint(value[2u]));
-  data.Store4((offset + 48u), asuint(value[3u]));
-})"},
-                    TypeCase{ty_mat2x2<f16>, R"({
-  data.Store<vector<float16_t, 2> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 2> >((offset + 4u), value[1u]);
-})"},
-                    TypeCase{ty_mat2x3<f16>, R"({
-  data.Store<vector<float16_t, 3> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 3> >((offset + 8u), value[1u]);
-})"},
-                    TypeCase{ty_mat2x4<f16>, R"({
-  data.Store<vector<float16_t, 4> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 4> >((offset + 8u), value[1u]);
-})"},
-                    TypeCase{ty_mat3x2<f16>, R"({
-  data.Store<vector<float16_t, 2> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 2> >((offset + 4u), value[1u]);
-  data.Store<vector<float16_t, 2> >((offset + 8u), value[2u]);
-})"},
-                    TypeCase{ty_mat3x3<f16>, R"({
-  data.Store<vector<float16_t, 3> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 3> >((offset + 8u), value[1u]);
-  data.Store<vector<float16_t, 3> >((offset + 16u), value[2u]);
-})"},
-                    TypeCase{ty_mat3x4<f16>, R"({
-  data.Store<vector<float16_t, 4> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 4> >((offset + 8u), value[1u]);
-  data.Store<vector<float16_t, 4> >((offset + 16u), value[2u]);
-})"},
-                    TypeCase{ty_mat4x2<f16>, R"({
-  data.Store<vector<float16_t, 2> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 2> >((offset + 4u), value[1u]);
-  data.Store<vector<float16_t, 2> >((offset + 8u), value[2u]);
-  data.Store<vector<float16_t, 2> >((offset + 12u), value[3u]);
-})"},
-                    TypeCase{ty_mat4x3<f16>, R"({
-  data.Store<vector<float16_t, 3> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 3> >((offset + 8u), value[1u]);
-  data.Store<vector<float16_t, 3> >((offset + 16u), value[2u]);
-  data.Store<vector<float16_t, 3> >((offset + 24u), value[3u]);
-})"},
-                    TypeCase{ty_mat4x4<f16>, R"({
-  data.Store<vector<float16_t, 4> >((offset + 0u), value[0u]);
-  data.Store<vector<float16_t, 4> >((offset + 8u), value[1u]);
-  data.Store<vector<float16_t, 4> >((offset + 16u), value[2u]);
-  data.Store<vector<float16_t, 4> >((offset + 24u), value[3u]);
-})"}));
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Store_Matrix_Empty) {
-    // struct Data {
-    //   a : f32,
-    //   b : mat2x3<f32>,
-    // };
-    // var<storage> data : Data;
-    // data.b = mat2x3<f32>();
-
-    SetupStorageBuffer(Vector{
-        Member("a", ty.i32()),
-        Member("b", ty.mat2x3<f32>()),
-    });
-
-    SetupFunction(Vector{
-        Assign(MemberAccessor("data", "b"), Call<mat2x3<f32>>()),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void data_store(uint offset, float2x3 value) {
-  data.Store3((offset + 0u), asuint(value[0u]));
-  data.Store3((offset + 16u), asuint(value[1u]));
-}
-
-void main() {
-  data_store(16u, float2x3((0.0f).xxx, (0.0f).xxx));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Load_Matrix_F32_Single_Element) {
-    // struct Data {
-    //   z : f32,
-    //   a : mat4x3<f32>,
-    // };
-    // var<storage> data : Data;
-    // data.a[2i][1i];
-
-    SetupStorageBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.mat4x3<f32>()),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", IndexAccessor(IndexAccessor(MemberAccessor("data", "a"), 2_i), 1_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  float x = asfloat(data.Load(52u));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Load_Matrix_F16_Single_Element) {
-    // struct Data {
-    //   z : f16,
-    //   a : mat4x3<f16>,
-    // };
-    // var<storage> data : Data;
-    // data.a[2i][1i];
-
-    Enable(wgsl::Extension::kF16);
-
-    SetupStorageBuffer(Vector{
-        Member("z", ty.f16()),
-        Member("a", ty.mat4x3<f16>()),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", IndexAccessor(IndexAccessor(MemberAccessor("data", "a"), 2_i), 1_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  float16_t x = data.Load<float16_t>(26u);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, UniformBuffer_Load_Matrix_F32_Single_Element) {
-    // struct Data {
-    //   z : f32,
-    //   a : mat4x3<f32>,
-    // };
-    // var<uniform> data : Data;
-    // data.a[2i][1i];
-
-    SetupUniformBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.mat4x3<f32>()),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", IndexAccessor(IndexAccessor(MemberAccessor("data", "a"), 2_i), 1_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[5];
-};
-
-void main() {
-  float x = asfloat(data[3].y);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, UniformBuffer_Load_Matrix_F16_Single_Element) {
-    // struct Data {
-    //   z : f16,
-    //   a : mat4x3<f16>,
-    // };
-    // var<uniform> data : Data;
-    // data.a[2i][1i];
-
-    Enable(wgsl::Extension::kF16);
-
-    SetupUniformBuffer(Vector{
-        Member("z", ty.f16()),
-        Member("a", ty.mat4x3<f16>()),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", IndexAccessor(IndexAccessor(MemberAccessor("data", "a"), 2_i), 1_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[3];
-};
-
-void main() {
-  float16_t x = float16_t(f16tof32(((data[1].z >> 16) & 0xFFFF)));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor,
-       EmitExpression_IndexAccessor_StorageBuffer_Load_I32_FromArray) {
-    // struct Data {
-    //   z : f32,
-    //   a : array<i32, 5i>,
-    // };
-    // var<storage> data : Data;
-    // data.a[2];
-
-    SetupStorageBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.array<i32, 5>()),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), 2_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  int x = asint(data.Load(12u));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor,
-       EmitExpression_IndexAccessor_UniformBuffer_Load_Vec4_I32_FromArray) {
-    // struct Data {
-    //   z : f32,
-    //   a : array<vec4<i32>, 5i>,
-    // };
-    // var<uniform> data : Data;
-    // data.a[2];
-
-    SetupUniformBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.array<vec4<i32>, 5>()),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), 2_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[6];
-};
-
-void main() {
-  int4 x = asint(data[3]);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor,
-       EmitExpression_IndexAccessor_StorageBuffer_Load_Struct_FromArray) {
-    // struct Inner {
-    //   @size(16i) @align(16i)
-    //   v : i32,
-    // };
-    // struct Data {
-    //   z : f32,
-    //   a : array<Inner, 5i>,
-    // };
-    // var<storage> data : Data;
-    // data.a[2i];
-
-    auto* elem_type =
-        Structure("Inner", Vector{
-                               Member("v", ty.i32(), Vector{MemberSize(16_i), MemberAlign(16_i)}),
-                           });
-
-    SetupStorageBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.array(ty.Of(elem_type), 5_i)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), 2_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(struct Inner {
-  int v;
-};
-
-RWByteAddressBuffer data : register(u0, space1);
-
-Inner data_load(uint offset) {
-  Inner tint_symbol = {asint(data.Load((offset + 0u)))};
-  return tint_symbol;
-}
-
-void main() {
-  Inner x = data_load(48u);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor,
-       EmitExpression_IndexAccessor_UniformBuffer_Load_Struct_FromArray) {
-    // struct Inner {
-    //   @size(16i) @align(16i)
-    //   v : i32,
-    // };
-    // struct Data {
-    //   z : f32,
-    //   a : array<Inner, 5i>,
-    // };
-    // var<uniform> data : Data;
-    // data.a[2i];
-
-    auto* elem_type =
-        Structure("Inner", Vector{
-                               Member("v", ty.i32(), Vector{MemberSize(16_i), MemberAlign(16_i)}),
-                           });
-
-    SetupUniformBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.array(ty.Of(elem_type), 5_i)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), 2_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(struct Inner {
-  int v;
-};
-
-cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[6];
-};
-
-Inner data_load(uint offset) {
-  const uint scalar_offset = ((offset + 0u)) / 4;
-  Inner tint_symbol = {asint(data[scalar_offset / 4][scalar_offset % 4])};
-  return tint_symbol;
-}
-
-void main() {
-  Inner x = data_load(48u);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor,
-       EmitExpression_IndexAccessor_StorageBuffer_Load_I32_FromArray_ExprIdx) {
-    // struct Data {
-    //   z : f32,
-    //   a : array<i32, 5i>,
-    // };
-    // var<storage> data : Data;
-    // data.a[(2i + 4i) - 3i];
-
-    SetupStorageBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.array<i32, 5>()),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("a", Expr(2_i))),
-        Decl(Var("b", Expr(4_i))),
-        Decl(Var("c", Expr(3_i))),
-        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), Sub(Add("a", "b"), "c")))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  int a = 2;
-  int b = 4;
-  int c = 3;
-  int x = asint(data.Load((4u + (4u * uint(((a + b) - c))))));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor,
-       EmitExpression_IndexAccessor_UniformBuffer_Load_Vec4_I32_FromArray_ExprIdx) {
-    // struct Data {
-    //   z : f32,
-    //   a : array<vec4<i32>, 5i>,
-    // };
-    // var<uniform> data : Data;
-    // data.a[(2i + 4i) - 3i];
-
-    SetupUniformBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.array<vec4<i32>, 5>()),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("a", Expr(2_i))),
-        Decl(Var("b", Expr(4_i))),
-        Decl(Var("c", Expr(3_i))),
-        Decl(Var("x", IndexAccessor(MemberAccessor("data", "a"), Sub(Add("a", "b"), "c")))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[6];
-};
-
-void main() {
-  int a = 2;
-  int b = 4;
-  int c = 3;
-  const uint scalar_offset = ((16u + (16u * uint(((a + b) - c))))) / 4;
-  int4 x = asint(data[scalar_offset / 4]);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Store_ToArray) {
-    // struct Data {
-    //   a : array<i32, 5i>,
-    // };
-    // var<storage> data : Data;
-    // data.a[2i] = 2i;
-
-    SetupStorageBuffer(Vector{
-        Member("z", ty.f32()),
-        Member("a", ty.array<i32, 5>()),
-    });
-
-    SetupFunction(Vector{
-        Assign(IndexAccessor(MemberAccessor("data", "a"), 2_i), 2_i),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  data.Store(12u, asuint(2));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Load_MultiLevel) {
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   c : array<Inner, 4u>,
-    // };
-    //
-    // var<storage> data : Data;
-    // data.c[2i].b
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupStorageBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  float3 x = asfloat(data.Load3(80u));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, UniformBuffer_Load_MultiLevel) {
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<storage> data : Data;
-    // data.c[2i].b
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupUniformBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x", MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[8];
-};
-
-void main() {
-  float3 x = asfloat(data[5].xyz);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Load_MultiLevel_Swizzle) {
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<storage> data : Data;
-    // data.c[2i].b.yx
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupStorageBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x",
-                 MemberAccessor(
-                     MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"), "yx"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  float2 x = asfloat(data.Load3(80u)).yx;
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, UniformBuffer_Load_MultiLevel_Swizzle) {
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<uniform> data : Data;
-    // data.c[2i].b.yx
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupUniformBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x",
-                 MemberAccessor(
-                     MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"), "yx"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[8];
-};
-
-void main() {
-  float2 x = asfloat(data[5].xyz).yx;
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor,
-       StorageBuffer_Load_MultiLevel_Swizzle_SingleLetter) {  // NOLINT
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<storage> data : Data;
-    // data.c[2i].b.g
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupStorageBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x",
-                 MemberAccessor(
-                     MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"), "g"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  float x = asfloat(data.Load(84u));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor,
-       UniformBuffer_Load_MultiLevel_Swizzle_SingleLetter) {  // NOLINT
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<uniform> data : Data;
-    // data.c[2i].b.g
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupUniformBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x",
-                 MemberAccessor(
-                     MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"), "g"))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[8];
-};
-
-void main() {
-  float x = asfloat(data[5].y);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Load_MultiLevel_Index) {
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<storage> data : Data;
-    // data.c[2i].b[1i]
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupStorageBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x",
-                 IndexAccessor(MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"),
-                               1_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  float x = asfloat(data.Load(84u));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, UniformBuffer_Load_MultiLevel_Index) {
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<uniform> data : Data;
-    // data.c[2i].b[1i]
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupUniformBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Decl(Var("x",
-                 IndexAccessor(MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"),
-                               1_i))),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(cbuffer cbuffer_data : register(b1, space1) {
-  uint4 data[8];
-};
-
-void main() {
-  float x = asfloat(data[5].y);
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Store_MultiLevel) {
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<storage> data : Pre;
-    // data.c[2i].b = vec3<f32>(1_f, 2_f, 3_f);
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupStorageBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Assign(MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"),
-               Call<vec3<f32>>(1_f, 2_f, 3_f)),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  data.Store3(80u, asuint(float3(1.0f, 2.0f, 3.0f)));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, StorageBuffer_Store_Swizzle_SingleLetter) {
-    // struct Inner {
-    //   a : vec3<i32>,
-    //   b : vec3<f32>,
-    // };
-    // struct Data {
-    //   var c : array<Inner, 4u>,
-    // };
-    //
-    // var<storage> data : Pre;
-    // data.c[2i].b.y = 1.f;
-
-    auto* inner = Structure("Inner", Vector{
-                                         Member("a", ty.vec3<i32>()),
-                                         Member("b", ty.vec3<f32>()),
-                                     });
-
-    SetupStorageBuffer(Vector{
-        Member("c", ty.array(ty.Of(inner), 4_u)),
-    });
-
-    SetupFunction(Vector{
-        Assign(MemberAccessor(MemberAccessor(IndexAccessor(MemberAccessor("data", "c"), 2_i), "b"),
-                              "y"),
-               Expr(1_f)),
-    });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    auto* expected =
-        R"(RWByteAddressBuffer data : register(u0, space1);
-
-void main() {
-  data.Store(84u, asuint(1.0f));
-  return;
-}
-)";
-    EXPECT_EQ(gen.Result(), expected);
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, Swizzle_xyz) {
-    auto* var = Var("my_vec", ty.vec4<f32>(), Call<vec4<f32>>(1_f, 2_f, 3_f, 4_f));
-    auto* expr = MemberAccessor("my_vec", "xyz");
-    WrapInFunction(var, expr);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("my_vec.xyz"));
-}
-
-TEST_F(HlslASTPrinterTest_MemberAccessor, Swizzle_gbr) {
-    auto* var = Var("my_vec", ty.vec4<f32>(), Call<vec4<f32>>(1_f, 2_f, 3_f, 4_f));
-    auto* expr = MemberAccessor("my_vec", "gbr");
-    WrapInFunction(var, expr);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("my_vec.gbr"));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/module_constant_test.cc b/src/tint/lang/hlsl/writer/ast_printer/module_constant_test.cc
deleted file mode 100644
index 17559d3..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/module_constant_test.cc
+++ /dev/null
@@ -1,259 +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/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/id_attribute.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using HlslASTPrinterTest_ModuleConstant = TestHelper;
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_AInt) {
-    auto* var = GlobalConst("G", Expr(1_a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  int l = 1;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_AFloat) {
-    auto* var = GlobalConst("G", Expr(1._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float l = 1.0f;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_i32) {
-    auto* var = GlobalConst("G", Expr(1_i));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  int l = 1;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_u32) {
-    auto* var = GlobalConst("G", Expr(1_u));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  uint l = 1u;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_f32) {
-    auto* var = GlobalConst("G", Expr(1_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float l = 1.0f;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = GlobalConst("G", Expr(1_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float16_t l = float16_t(1.0h);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_vec3_AInt) {
-    auto* var = GlobalConst("G", Call<vec3<Infer>>(1_a, 2_a, 3_a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  int3 l = int3(1, 2, 3);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_vec3_AFloat) {
-    auto* var = GlobalConst("G", Call<vec3<Infer>>(1._a, 2._a, 3._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float3 l = float3(1.0f, 2.0f, 3.0f);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_vec3_f32) {
-    auto* var = GlobalConst("G", Call<vec3<f32>>(1_f, 2_f, 3_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float3 l = float3(1.0f, 2.0f, 3.0f);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_vec3_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = GlobalConst("G", Call<vec3<f16>>(1_h, 2_h, 3_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  vector<float16_t, 3> l = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_mat2x3_AFloat) {
-    auto* var = GlobalConst("G", Call<mat2x3<Infer>>(1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_mat2x3_f32) {
-    auto* var = GlobalConst("G", Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_mat2x3_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = GlobalConst("G", Call<mat2x3<f16>>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  matrix<float16_t, 2, 3> l = matrix<float16_t, 2, 3>(vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h)), vector<float16_t, 3>(float16_t(4.0h), float16_t(5.0h), float16_t(6.0h)));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_arr_f32) {
-    auto* var = GlobalConst("G", Call<array<f32, 3>>(1_f, 2_f, 3_f));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float l[3] = {1.0f, 2.0f, 3.0f};
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_ModuleConstant, Emit_GlobalConst_arr_vec2_bool) {
-    auto* var = GlobalConst("G", Call<array<vec2<bool>, 3>>(         //
-                                     Call<vec2<bool>>(true, false),  //
-                                     Call<vec2<bool>>(false, true),  //
-                                     Call<vec2<bool>>(true, true)));
-    Func("f", tint::Empty, ty.void_(), Vector{Decl(Let("l", Expr(var)))});
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  bool2 l[3] = {bool2(true, false), bool2(false, true), (true).xx};
-}
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/return_test.cc b/src/tint/lang/hlsl/writer/ast_printer/return_test.cc
deleted file mode 100644
index 8cb83da..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/return_test.cc
+++ /dev/null
@@ -1,62 +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/hlsl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Return = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Return, Emit_Return) {
-    auto* r = Return();
-    WrapInFunction(r);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  return;\n");
-}
-
-TEST_F(HlslASTPrinterTest_Return, Emit_ReturnWithValue) {
-    auto* r = Return(123_i);
-    Func("f", tint::Empty, ty.i32(), Vector{r});
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(r)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  return 123;\n");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/sanitizer_test.cc b/src/tint/lang/hlsl/writer/ast_printer/sanitizer_test.cc
deleted file mode 100644
index 72bfc3b..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/sanitizer_test.cc
+++ /dev/null
@@ -1,325 +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/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using HlslSanitizerTest = TestHelper;
-
-TEST_F(HlslSanitizerTest, Call_ArrayLength) {
-    auto* s = Structure("my_struct", Vector{Member(0, "a", ty.array<f32>())});
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(2_a));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("len", ty.u32(), Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
-
-void a_func() {
-  uint tint_symbol_1 = 0u;
-  b.GetDimensions(tint_symbol_1);
-  uint tint_symbol_2 = ((tint_symbol_1 - 0u) / 4u);
-  uint len = tint_symbol_2;
-  return;
-}
-)";
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(HlslSanitizerTest, Call_ArrayLength_OtherMembersInStruct) {
-    auto* s = Structure("my_struct", Vector{
-                                         Member(0, "z", ty.f32()),
-                                         Member(4, "a", ty.array<f32>()),
-                                     });
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(2_a));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("len", ty.u32(), Call("arrayLength", AddressOf(MemberAccessor("b", "a"))))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
-
-void a_func() {
-  uint tint_symbol_1 = 0u;
-  b.GetDimensions(tint_symbol_1);
-  uint tint_symbol_2 = ((tint_symbol_1 - 4u) / 4u);
-  uint len = tint_symbol_2;
-  return;
-}
-)";
-
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(HlslSanitizerTest, Call_ArrayLength_ViaLets) {
-    auto* s = Structure("my_struct", Vector{Member(0, "a", ty.array<f32>())});
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(2_a));
-
-    auto* p = Let("p", AddressOf("b"));
-    auto* p2 = Let("p2", AddressOf(MemberAccessor(Deref(p), "a")));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(p),
-             Decl(p2),
-             Decl(Var("len", ty.u32(), Call("arrayLength", p2))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(ByteAddressBuffer b : register(t1, space2);
-
-void a_func() {
-  uint tint_symbol_1 = 0u;
-  b.GetDimensions(tint_symbol_1);
-  uint tint_symbol_2 = ((tint_symbol_1 - 0u) / 4u);
-  uint len = tint_symbol_2;
-  return;
-}
-)";
-
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(HlslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) {
-    auto* s = Structure("my_struct", Vector{Member(0, "a", ty.array<f32>())});
-    GlobalVar("b", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(1_a),
-              Group(2_a));
-    GlobalVar("c", ty.Of(s), core::AddressSpace::kStorage, core::Access::kRead, Binding(2_a),
-              Group(2_a));
-
-    Func("a_func", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("len", ty.u32(),
-                      Add(Call("arrayLength", AddressOf(MemberAccessor("b", "a"))),
-                          Call("arrayLength", AddressOf(MemberAccessor("c", "a")))))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    Options options;
-    options.array_length_from_uniform.ubo_binding = {3, 4};
-    options.array_length_from_uniform.bindpoint_to_size_index.emplace(BindingPoint{2, 2}, 7u);
-    ASTPrinter& gen = SanitizeAndBuild(options);
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(cbuffer cbuffer_tint_array_lengths : register(b4, space3) {
-  uint4 tint_array_lengths[2];
-};
-ByteAddressBuffer b : register(t1, space2);
-ByteAddressBuffer c : register(t2, space2);
-
-void a_func() {
-  uint tint_symbol_1 = 0u;
-  b.GetDimensions(tint_symbol_1);
-  uint tint_symbol_2 = ((tint_symbol_1 - 0u) / 4u);
-  uint len = (tint_symbol_2 + ((tint_array_lengths[1].w - 0u) / 4u));
-  return;
-}
-)";
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(HlslSanitizerTest, PromoteArrayInitializerToConstVar) {
-    auto* array_init = Call<array<i32, 4>>(1_i, 2_i, 3_i, 4_i);
-
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("idx", Expr(3_i))),
-             Decl(Var("pos", ty.i32(), IndexAccessor(array_init, "idx"))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(void main() {
-  int idx = 3;
-  int tint_symbol[4] = {1, 2, 3, 4};
-  int pos = tint_symbol[idx];
-  return;
-}
-)";
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(HlslSanitizerTest, PromoteStructInitializerToConstVar) {
-    auto* runtime_value = Var("runtime_value", Expr(3_f));
-    auto* str = Structure("S", Vector{
-                                   Member("a", ty.i32()),
-                                   Member("b", ty.vec3<f32>()),
-                                   Member("c", ty.i32()),
-                               });
-    auto* struct_init = Call(ty.Of(str), 1_i, Call<vec3<f32>>(2_f, runtime_value, 4_f), 4_i);
-    auto* struct_access = MemberAccessor(struct_init, "b");
-    auto* pos = Var("pos", ty.vec3<f32>(), struct_access);
-
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(runtime_value),
-             Decl(pos),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(struct S {
-  int a;
-  float3 b;
-  int c;
-};
-
-void main() {
-  float runtime_value = 3.0f;
-  S tint_symbol = {1, float3(2.0f, runtime_value, 4.0f), 4};
-  float3 pos = tint_symbol.b;
-  return;
-}
-)";
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(HlslSanitizerTest, SimplifyPointersBasic) {
-    // var v : i32;
-    // let p : ptr<function, i32> = &v;
-    // let x : i32 = *p;
-    auto* v = Var("v", ty.i32());
-    auto* p = Let("p", ty.ptr<function, i32>(), AddressOf(v));
-    auto* x = Var("x", ty.i32(), Deref(p));
-
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(v),
-             Decl(p),
-             Decl(x),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(void main() {
-  int v = 0;
-  int x = v;
-  return;
-}
-)";
-    EXPECT_EQ(expect, got);
-}
-
-TEST_F(HlslSanitizerTest, SimplifyPointersComplexChain) {
-    // var a : array<mat4x4<f32>, 4u>;
-    // let ap : ptr<function, array<mat4x4<f32>, 4u>> = &a;
-    // let mp : ptr<function, mat4x4<f32>> = &(*ap)[3i];
-    // let vp : ptr<function, vec4<f32>> = &(*mp)[2i];
-    // let v : vec4<f32> = *vp;
-    auto* a = Var("a", ty.array(ty.mat4x4<f32>(), 4_u));
-    auto* ap = Let("ap", ty.ptr<function, array<mat4x4<f32>, 4>>(), AddressOf(a));
-    auto* mp = Let("mp", ty.ptr<function, mat4x4<f32>>(), AddressOf(IndexAccessor(Deref(ap), 3_i)));
-    auto* vp = Let("vp", ty.ptr<function, vec4<f32>>(), AddressOf(IndexAccessor(Deref(mp), 2_i)));
-    auto* v = Var("v", ty.vec4<f32>(), Deref(vp));
-
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(a),
-             Decl(ap),
-             Decl(mp),
-             Decl(vp),
-             Decl(v),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    auto got = gen.Result();
-    auto* expect = R"(void main() {
-  float4x4 a[4] = (float4x4[4])0;
-  float4 v = a[3][2];
-  return;
-}
-)";
-    EXPECT_EQ(expect, got);
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/switch_test.cc b/src/tint/lang/hlsl/writer/ast_printer/switch_test.cc
deleted file mode 100644
index 1f18804..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/switch_test.cc
+++ /dev/null
@@ -1,150 +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/hlsl/writer/ast_printer/helper_test.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Switch = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Switch, Emit_Switch) {
-    GlobalVar("cond", ty.i32(), core::AddressSpace::kPrivate);
-    auto* s = Switch(                             //
-        Expr("cond"),                             //
-        Case(CaseSelector(5_i), Block(Break())),  //
-        DefaultCase());
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  switch(cond) {
-    case 5: {
-      break;
-    }
-    default: {
-      break;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Switch, Emit_Switch_MixedDefault) {
-    GlobalVar("cond", ty.i32(), core::AddressSpace::kPrivate);
-    auto* s = Switch(  //
-        Expr("cond"),  //
-        Case(Vector{CaseSelector(5_i), DefaultCaseSelector()}, Block(Break())));
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  switch(cond) {
-    case 5:
-    default: {
-      break;
-    }
-  }
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Switch, Emit_Switch_OnlyDefaultCase_NoSideEffectsCondition) {
-    // var<private> cond : i32;
-    // var<private> a : i32;
-    // fn test() {
-    //   switch(cond) {
-    //     default: {
-    //       a = 42;
-    //     }
-    //   }
-    // }
-    GlobalVar("cond", ty.i32(), core::AddressSpace::kPrivate);
-    GlobalVar("a", ty.i32(), core::AddressSpace::kPrivate);
-    auto* s = Switch(  //
-        Expr("cond"),  //
-        DefaultCase(Block(Assign(Expr("a"), Expr(42_i)))));
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  do {
-    a = 42;
-  } while (false);
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Switch, Emit_Switch_OnlyDefaultCase_SideEffectsCondition) {
-    // var<private> global : i32;
-    // fn bar() -> i32 {
-    //   global = 84;
-    //   return global;
-    // }
-    //
-    // var<private> a : i32;
-    // fn test() {
-    //   switch(bar()) {
-    //     default: {
-    //       a = 42;
-    //     }
-    //   }
-    // }
-    GlobalVar("global", ty.i32(), core::AddressSpace::kPrivate);
-    Func("bar", {}, ty.i32(),
-         Vector{                               //
-                Assign("global", Expr(84_i)),  //
-                Return("global")});
-
-    GlobalVar("a", ty.i32(), core::AddressSpace::kPrivate);
-    auto* s = Switch(  //
-        Call("bar"),   //
-        DefaultCase(Block(Assign(Expr("a"), Expr(42_i)))));
-    WrapInFunction(s);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(s)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(  bar();
-  do {
-    a = 42;
-  } while (false);
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/type_test.cc b/src/tint/lang/hlsl/writer/ast_printer/type_test.cc
deleted file mode 100644
index ee40355..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/type_test.cc
+++ /dev/null
@@ -1,606 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/core/type/depth_texture.h"
-#include "src/tint/lang/core/type/multisampled_texture.h"
-#include "src/tint/lang/core/type/sampled_texture.h"
-#include "src/tint/lang/core/type/sampler.h"
-#include "src/tint/lang/core/type/storage_texture.h"
-#include "src/tint/lang/core/type/texture_dimension.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using ::testing::HasSubstr;
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_Type = TestHelper;
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Array) {
-    auto arr = ty.array<bool, 4>();
-    ast::Type ty = GlobalVar("G", arr, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), core::AddressSpace::kUndefined,
-                             core::Access::kReadWrite, "ary"))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "bool ary[4]");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_ArrayOfArray) {
-    auto arr = ty.array(ty.array<bool, 4>(), 5_u);
-    ast::Type ty = GlobalVar("G", arr, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), core::AddressSpace::kUndefined,
-                             core::Access::kReadWrite, "ary"))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "bool ary[5][4]");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_ArrayOfArrayOfArray) {
-    auto arr = ty.array(ty.array(ty.array<bool, 4>(), 5_u), 6_u);
-    ast::Type ty = GlobalVar("G", arr, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), core::AddressSpace::kUndefined,
-                             core::Access::kReadWrite, "ary"))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "bool ary[6][5][4]");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Array_WithoutName) {
-    auto arr = ty.array<bool, 4>();
-    ast::Type ty = GlobalVar("G", arr, core::AddressSpace::kPrivate)->type;
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(ty), core::AddressSpace::kUndefined,
-                             core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "bool[4]");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Bool) {
-    auto* bool_ = create<core::type::Bool>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, bool_, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "bool");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_F16) {
-    auto* f16 = create<core::type::F16>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, f16, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float16_t");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_F32) {
-    auto* f32 = create<core::type::F32>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, f32, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_I32) {
-    auto* i32 = create<core::type::I32>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, i32, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "int");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Matrix_F16) {
-    auto* f16 = create<core::type::F16>();
-    auto* vec3 = create<core::type::Vector>(f16, 3u);
-    auto* mat2x3 = create<core::type::Matrix>(vec3, 2u);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, mat2x3, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "matrix<float16_t, 2, 3>");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Matrix_F32) {
-    auto* f32 = create<core::type::F32>();
-    auto* vec3 = create<core::type::Vector>(f32, 3u);
-    auto* mat2x3 = create<core::type::Matrix>(vec3, 2u);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, mat2x3, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float2x3");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_StructDecl) {
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", ty.f32()),
-                             });
-    GlobalVar("g", ty.Of(s), core::AddressSpace::kPrivate);
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(s)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-    EXPECT_EQ(buf.String(), R"(struct S {
-  int a;
-  float b;
-};
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_StructDecl_OmittedIfStorageBuffer) {
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", ty.f32()),
-                             });
-    GlobalVar("g", ty.Of(s), core::AddressSpace::kStorage, core::Access::kReadWrite, Binding(0_a),
-              Group(0_a));
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "RWByteAddressBuffer g : register(u0);\n");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Struct) {
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32()),
-                                 Member("b", ty.f32()),
-                             });
-    GlobalVar("g", ty.Of(s), core::AddressSpace::kPrivate);
-
-    ASTPrinter& gen = Build();
-
-    auto* str = program->TypeOf(s)->As<core::type::Struct>();
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, str, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "S");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Struct_NameCollision) {
-    auto* s = Structure("S", Vector{
-                                 Member("double", ty.i32()),
-                                 Member("float", ty.f32()),
-                             });
-    GlobalVar("g", ty.Of(s), core::AddressSpace::kPrivate);
-
-    ASTPrinter& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(R"(struct S {
-  int tint_symbol;
-  float tint_symbol_1;
-};
-)"));
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Struct_WithOffsetAttributes) {
-    auto* s = Structure("S", Vector{
-                                 Member("a", ty.i32(), Vector{MemberOffset(0_a)}),
-                                 Member("b", ty.f32(), Vector{MemberOffset(8_a)}),
-                             });
-    GlobalVar("g", ty.Of(s), core::AddressSpace::kPrivate);
-
-    ASTPrinter& gen = Build();
-
-    tint::TextGenerator::TextBuffer buf;
-    auto* str = program->TypeOf(s)->As<core::type::Struct>();
-    ASSERT_TRUE(gen.EmitStructType(&buf, str)) << gen.Diagnostics();
-    EXPECT_EQ(buf.String(), R"(struct S {
-  int a;
-  float b;
-};
-)");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_U32) {
-    auto* u32 = create<core::type::U32>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, u32, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "uint");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Vector) {
-    auto* f32 = create<core::type::F32>();
-    auto* vec3 = create<core::type::Vector>(f32, 3u);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, vec3, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "float3");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitType_Void) {
-    auto* void_ = create<core::type::Void>();
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, void_, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "void");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitSampler) {
-    auto* sampler = create<core::type::Sampler>(core::type::SamplerKind::kSampler);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, sampler, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "SamplerState");
-}
-
-TEST_F(HlslASTPrinterTest_Type, EmitSamplerComparison) {
-    auto* sampler = create<core::type::Sampler>(core::type::SamplerKind::kComparisonSampler);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(
-        gen.EmitType(out, sampler, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "SamplerComparisonState");
-}
-
-struct HlslDepthTextureData {
-    core::type::TextureDimension dim;
-    std::string result;
-};
-inline std::ostream& operator<<(std::ostream& out, HlslDepthTextureData data) {
-    StringStream str;
-    str << data.dim;
-    out << str.str();
-    return out;
-}
-using HlslDepthTexturesTest = TestParamHelper<HlslDepthTextureData>;
-TEST_P(HlslDepthTexturesTest, Emit) {
-    auto params = GetParam();
-
-    auto t = ty.depth_texture(params.dim);
-
-    GlobalVar("tex", t, Binding(1_a), Group(2_a));
-
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("v", Call("textureDimensions", "tex"))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(params.result));
-}
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest_Type,
-    HlslDepthTexturesTest,
-    testing::Values(HlslDepthTextureData{core::type::TextureDimension::k2d,
-                                         "Texture2D tex : register(t1, space2);"},
-                    HlslDepthTextureData{core::type::TextureDimension::k2dArray,
-                                         "Texture2DArray tex : register(t1, space2);"},
-                    HlslDepthTextureData{core::type::TextureDimension::kCube,
-                                         "TextureCube tex : register(t1, space2);"},
-                    HlslDepthTextureData{core::type::TextureDimension::kCubeArray,
-                                         "TextureCubeArray tex : register(t1, space2);"}));
-
-using HlslDepthMultisampledTexturesTest = TestHelper;
-TEST_F(HlslDepthMultisampledTexturesTest, Emit) {
-    auto t = ty.depth_multisampled_texture(core::type::TextureDimension::k2d);
-
-    GlobalVar("tex", t, Binding(1_a), Group(2_a));
-
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("v", Call("textureDimensions", "tex"))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("Texture2DMS<float4> tex : register(t1, space2);"));
-}
-
-enum class TextureDataType { F32, U32, I32 };
-struct HlslSampledTextureData {
-    core::type::TextureDimension dim;
-    TextureDataType datatype;
-    std::string result;
-};
-inline std::ostream& operator<<(std::ostream& out, HlslSampledTextureData data) {
-    StringStream str;
-    str << data.dim;
-    out << str.str();
-    return out;
-}
-using HlslSampledTexturesTest = TestParamHelper<HlslSampledTextureData>;
-TEST_P(HlslSampledTexturesTest, Emit) {
-    auto params = GetParam();
-
-    ast::Type datatype;
-    switch (params.datatype) {
-        case TextureDataType::F32:
-            datatype = ty.f32();
-            break;
-        case TextureDataType::U32:
-            datatype = ty.u32();
-            break;
-        case TextureDataType::I32:
-            datatype = ty.i32();
-            break;
-    }
-    ast::Type t = ty.sampled_texture(params.dim, datatype);
-
-    GlobalVar("tex", t, Binding(1_a), Group(2_a));
-
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("v", Call("textureDimensions", "tex"))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(params.result));
-}
-INSTANTIATE_TEST_SUITE_P(HlslASTPrinterTest_Type,
-                         HlslSampledTexturesTest,
-                         testing::Values(
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k1d,
-                                 TextureDataType::F32,
-                                 "Texture1D<float4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k2d,
-                                 TextureDataType::F32,
-                                 "Texture2D<float4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k2dArray,
-                                 TextureDataType::F32,
-                                 "Texture2DArray<float4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k3d,
-                                 TextureDataType::F32,
-                                 "Texture3D<float4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::kCube,
-                                 TextureDataType::F32,
-                                 "TextureCube<float4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::kCubeArray,
-                                 TextureDataType::F32,
-                                 "TextureCubeArray<float4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k1d,
-                                 TextureDataType::U32,
-                                 "Texture1D<uint4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k2d,
-                                 TextureDataType::U32,
-                                 "Texture2D<uint4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k2dArray,
-                                 TextureDataType::U32,
-                                 "Texture2DArray<uint4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k3d,
-                                 TextureDataType::U32,
-                                 "Texture3D<uint4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::kCube,
-                                 TextureDataType::U32,
-                                 "TextureCube<uint4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::kCubeArray,
-                                 TextureDataType::U32,
-                                 "TextureCubeArray<uint4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k1d,
-                                 TextureDataType::I32,
-                                 "Texture1D<int4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k2d,
-                                 TextureDataType::I32,
-                                 "Texture2D<int4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k2dArray,
-                                 TextureDataType::I32,
-                                 "Texture2DArray<int4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::k3d,
-                                 TextureDataType::I32,
-                                 "Texture3D<int4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::kCube,
-                                 TextureDataType::I32,
-                                 "TextureCube<int4> tex : register(t1, space2);",
-                             },
-                             HlslSampledTextureData{
-                                 core::type::TextureDimension::kCubeArray,
-                                 TextureDataType::I32,
-                                 "TextureCubeArray<int4> tex : register(t1, space2);",
-                             }));
-
-TEST_F(HlslASTPrinterTest_Type, EmitMultisampledTexture) {
-    auto* f32 = create<core::type::F32>();
-    auto* s = create<core::type::MultisampledTexture>(core::type::TextureDimension::k2d, f32);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitType(out, s, core::AddressSpace::kUndefined, core::Access::kReadWrite, ""))
-        << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "Texture2DMS<float4>");
-}
-
-struct HlslStorageTextureData {
-    core::type::TextureDimension dim;
-    core::TexelFormat imgfmt;
-    std::string result;
-};
-inline std::ostream& operator<<(std::ostream& out, HlslStorageTextureData data) {
-    StringStream str;
-    str << data.dim;
-    out << str.str();
-    return out;
-}
-using HlslStorageTexturesTest = TestParamHelper<HlslStorageTextureData>;
-TEST_P(HlslStorageTexturesTest, Emit) {
-    auto params = GetParam();
-
-    auto t = ty.storage_texture(params.dim, params.imgfmt, core::Access::kWrite);
-
-    GlobalVar("tex", t,
-              Vector{
-                  Group(2_a),
-                  Binding(1_a),
-              });
-
-    Func("main", tint::Empty, ty.void_(),
-         Vector{
-             Decl(Var("v", Call("textureDimensions", "tex"))),
-         },
-         Vector{
-             Stage(ast::PipelineStage::kFragment),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr(params.result));
-}
-INSTANTIATE_TEST_SUITE_P(
-    HlslASTPrinterTest_Type,
-    HlslStorageTexturesTest,
-    testing::Values(
-        HlslStorageTextureData{core::type::TextureDimension::k1d, core::TexelFormat::kRgba8Unorm,
-                               "RWTexture1D<float4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k2d, core::TexelFormat::kRgba16Float,
-                               "RWTexture2D<float4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k2dArray, core::TexelFormat::kR32Float,
-                               "RWTexture2DArray<float4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k3d, core::TexelFormat::kRg32Float,
-                               "RWTexture3D<float4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k1d, core::TexelFormat::kRgba32Float,
-                               "RWTexture1D<float4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k2d, core::TexelFormat::kRgba16Uint,
-                               "RWTexture2D<uint4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k2dArray, core::TexelFormat::kR32Uint,
-                               "RWTexture2DArray<uint4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k3d, core::TexelFormat::kRg32Uint,
-                               "RWTexture3D<uint4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k1d, core::TexelFormat::kRgba32Uint,
-                               "RWTexture1D<uint4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k2d, core::TexelFormat::kRgba16Sint,
-                               "RWTexture2D<int4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k2dArray, core::TexelFormat::kR32Sint,
-                               "RWTexture2DArray<int4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k3d, core::TexelFormat::kRg32Sint,
-                               "RWTexture3D<int4> tex : register(u1, space2);"},
-        HlslStorageTextureData{core::type::TextureDimension::k1d, core::TexelFormat::kRgba32Sint,
-                               "RWTexture1D<int4> tex : register(u1, space2);"}));
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/unary_op_test.cc b/src/tint/lang/hlsl/writer/ast_printer/unary_op_test.cc
deleted file mode 100644
index 3e841aa..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/unary_op_test.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/utils/text/string_stream.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslUnaryOpTest = TestHelper;
-
-TEST_F(HlslUnaryOpTest, AddressOf) {
-    GlobalVar("expr", ty.f32(), core::AddressSpace::kPrivate);
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kAddressOf, Expr("expr"));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "expr");
-}
-
-TEST_F(HlslUnaryOpTest, Complement) {
-    GlobalVar("expr", ty.u32(), core::AddressSpace::kPrivate);
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kComplement, Expr("expr"));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "~(expr)");
-}
-
-TEST_F(HlslUnaryOpTest, Indirection) {
-    GlobalVar("G", ty.f32(), core::AddressSpace::kPrivate);
-    auto* p = Let("expr", create<ast::UnaryOpExpression>(core::UnaryOp::kAddressOf, Expr("G")));
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kIndirection, Expr("expr"));
-    WrapInFunction(p, op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "expr");
-}
-
-TEST_F(HlslUnaryOpTest, Not) {
-    GlobalVar("expr", ty.bool_(), core::AddressSpace::kPrivate);
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kNot, Expr("expr"));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "!(expr)");
-}
-
-TEST_F(HlslUnaryOpTest, Negation) {
-    GlobalVar("expr", ty.i32(), core::AddressSpace::kPrivate);
-    auto* op = create<ast::UnaryOpExpression>(core::UnaryOp::kNegation, Expr("expr"));
-    WrapInFunction(op);
-
-    ASTPrinter& gen = Build();
-
-    StringStream out;
-    ASSERT_TRUE(gen.EmitExpression(out, op)) << gen.Diagnostics();
-    EXPECT_EQ(out.str(), "-(expr)");
-}
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/variable_decl_statement_test.cc b/src/tint/lang/hlsl/writer/ast_printer/variable_decl_statement_test.cc
deleted file mode 100644
index e546046..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/variable_decl_statement_test.cc
+++ /dev/null
@@ -1,589 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
-#include "gmock/gmock.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-using ::testing::HasSubstr;
-
-using HlslASTPrinterTest_VariableDecl = TestHelper;
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement) {
-    auto* var = Var("a", ty.f32());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  float a = 0.0f;\n");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Let) {
-    auto* var = Let("a", ty.f32(), Call<f32>());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "  float a = 0.0f;\n");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const) {
-    auto* var = Const("a", ty.f32(), Call<f32>());
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), "");  // Not a mistake - 'const' is inlined
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_AInt) {
-    auto* C = Const("C", Expr(1_a));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  int l = 1;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_AFloat) {
-    auto* C = Const("C", Expr(1._a));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float l = 1.0f;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_i32) {
-    auto* C = Const("C", Expr(1_i));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  int l = 1;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_u32) {
-    auto* C = Const("C", Expr(1_u));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  uint l = 1u;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_f32) {
-    auto* C = Const("C", Expr(1_f));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float l = 1.0f;
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* C = Const("C", Expr(1_h));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float16_t l = float16_t(1.0h);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AInt) {
-    auto* C = Const("C", Call<vec3<Infer>>(1_a, 2_a, 3_a));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  int3 l = int3(1, 2, 3);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AFloat) {
-    auto* C = Const("C", Call<vec3<Infer>>(1._a, 2._a, 3._a));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float3 l = float3(1.0f, 2.0f, 3.0f);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_f32) {
-    auto* C = Const("C", Call<vec3<f32>>(1_f, 2_f, 3_f));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float3 l = float3(1.0f, 2.0f, 3.0f);
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* C = Const("C", Call<vec3<f16>>(1_h, 2_h, 3_h));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  vector<float16_t, 3> l = vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
-    auto* C = Const("C", Call<mat2x3<Infer>>(1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_f32) {
-    auto* C = Const("C", Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_f16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* C = Const("C", Call<mat2x3<f16>>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  matrix<float16_t, 2, 3> l = matrix<float16_t, 2, 3>(vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h)), vector<float16_t, 3>(float16_t(4.0h), float16_t(5.0h), float16_t(6.0h)));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_f32) {
-    auto* C = Const("C", Call(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float l[3] = {1.0f, 2.0f, 3.0f};
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
-    auto* C = Const("C", Call<array<vec2<bool>, 3>>(         //
-                             Call<vec2<bool>>(true, false),  //
-                             Call<vec2<bool>>(false, true),  //
-                             Call<vec2<bool>>(true, true)));
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  bool2 l[3] = {bool2(true, false), bool2(false, true), (true).xx};
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Array) {
-    auto* var = Var("a", ty.array<f32, 5>());
-
-    WrapInFunction(var, Expr("a"));
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("  float a[5] = (float[5])0;\n"));
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Private) {
-    GlobalVar("a", ty.f32(), core::AddressSpace::kPrivate);
-
-    WrapInFunction(Expr("a"));
-
-    ASTPrinter& gen = Build();
-
-    gen.IncrementIndent();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("  static float a = 0.0f;\n"));
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec_F32) {
-    auto* var = Var("a", ty.vec3<f32>(), Call<vec3<f32>>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(float3 a = (0.0f).xxx;
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = Var("a", ty.vec3<f16>(), Call<vec3<f16>>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(), R"(vector<float16_t, 3> a = (float16_t(0.0h)).xxx;
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroMat_F32) {
-    auto* var = Var("a", ty.mat2x3<f32>(), Call<mat2x3<f32>>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(gen.Result(),
-              R"(float2x3 a = float2x3((0.0f).xxx, (0.0f).xxx);
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroMat_F16) {
-    Enable(wgsl::Extension::kF16);
-
-    auto* var = Var("a", ty.mat2x3<f16>(), Call<mat2x3<f16>>());
-
-    auto* stmt = Decl(var);
-    WrapInFunction(stmt);
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.Diagnostics();
-    EXPECT_EQ(
-        gen.Result(),
-        R"(matrix<float16_t, 2, 3> a = matrix<float16_t, 2, 3>((float16_t(0.0h)).xxx, (float16_t(0.0h)).xxx);
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_Mat) {
-    auto* C = Const("C", Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
-
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_Struct_of_Mat) {
-    Structure("S", Vector{Member("m", ty.mat2x3<f32>())});
-    auto* C = Const("C", Call("S", Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f)));
-
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(struct S {
-  float2x3 m;
-};
-
-void f() {
-  S l = {float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))};
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_Struct_of_Struct_of_Mat) {
-    Structure("S", Vector{Member("m", ty.mat2x3<f32>())});
-    Structure("S2", Vector{Member("s", ty("S"))});
-    auto* C = Const("C", Call("S2", Call("S", Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f))));
-
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(struct S {
-  float2x3 m;
-};
-struct S2 {
-  S s;
-};
-
-void f() {
-  S2 l = {{float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))}};
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_Struct_of_Array_of_Mat) {
-    Structure("S", Vector{Member("m", ty.array(ty.mat2x3<f32>(), 1_u))});
-
-    auto* C = Const("C", Call("S", Call(ty.array(ty.mat2x3<f32>(), 1_u),
-                                        Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f))));
-
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(struct S {
-  float2x3 m[1];
-};
-
-void f() {
-  S l = {{float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))}};
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_Array_of_Mat) {
-    auto* C = Const("C", Call(ty.array(ty.mat2x3<f32>(), 1_u),
-                              Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f)));
-
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(void f() {
-  float2x3 l[1] = {float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))};
-}
-)");
-}
-
-TEST_F(HlslASTPrinterTest_VariableDecl, Emit_VariableDeclStatement_Const_Array_of_Struct_of_Mat) {
-    Structure("S", Vector{Member("m", ty.mat2x3<f32>())});
-
-    auto* C = Const("C", Call(ty.array(ty("S"), 1_u),
-                              Call(ty("S"), Call<mat2x3<f32>>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f))));
-
-    Func("f", tint::Empty, ty.void_(),
-         Vector{
-             Decl(C),
-             Decl(Let("l", Expr(C))),
-         });
-
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-
-    EXPECT_EQ(gen.Result(), R"(struct S {
-  float2x3 m;
-};
-
-void f() {
-  S l[1] = {{float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))}};
-}
-)");
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_printer/workgroup_var_test.cc b/src/tint/lang/hlsl/writer/ast_printer/workgroup_var_test.cc
deleted file mode 100644
index c8990a5..0000000
--- a/src/tint/lang/hlsl/writer/ast_printer/workgroup_var_test.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2021 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/helper_test.h"
-#include "src/tint/lang/wgsl/ast/id_attribute.h"
-#include "src/tint/lang/wgsl/ast/stage_attribute.h"
-
-using ::testing::HasSubstr;
-
-using namespace tint::core::number_suffixes;  // NOLINT
-
-namespace tint::hlsl::writer {
-namespace {
-
-using HlslASTPrinterTest_WorkgroupVar = TestHelper;
-
-TEST_F(HlslASTPrinterTest_WorkgroupVar, Basic) {
-    GlobalVar("wg", ty.f32(), core::AddressSpace::kWorkgroup);
-
-    Func("main", tint::Empty, ty.void_(), Vector{Assign("wg", 1.2_f)},
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("groupshared float wg;\n"));
-}
-
-TEST_F(HlslASTPrinterTest_WorkgroupVar, Aliased) {
-    auto* alias = Alias("F32", ty.f32());
-
-    GlobalVar("wg", ty.Of(alias), core::AddressSpace::kWorkgroup);
-
-    Func("main", tint::Empty, ty.void_(), Vector{Assign("wg", 1.2_f)},
-         Vector{
-             Stage(ast::PipelineStage::kCompute),
-             WorkgroupSize(1_i),
-         });
-    ASTPrinter& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.Diagnostics();
-    EXPECT_THAT(gen.Result(), HasSubstr("groupshared float wg;\n"));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/BUILD.bazel b/src/tint/lang/hlsl/writer/ast_raise/BUILD.bazel
deleted file mode 100644
index 4eaab70..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/BUILD.bazel
+++ /dev/null
@@ -1,173 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.bazel.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-load("//src/tint:flags.bzl", "COPTS")
-load("@bazel_skylib//lib:selects.bzl", "selects")
-cc_library(
-  name = "ast_raise",
-  srcs = [
-    "calculate_array_length.cc",
-    "decompose_memory_access.cc",
-    "localize_struct_array_assignment.cc",
-    "num_workgroups_from_uniform.cc",
-    "pixel_local.cc",
-    "truncate_interstage_variables.cc",
-  ],
-  hdrs = [
-    "calculate_array_length.h",
-    "decompose_memory_access.h",
-    "localize_struct_array_assignment.h",
-    "num_workgroups_from_uniform.h",
-    "pixel_local.h",
-    "truncate_interstage_variables.h",
-  ],
-  deps = [
-    "//src/tint/api/common",
-    "//src/tint/lang/core",
-    "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/type",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast/transform",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/resolver",
-    "//src/tint/lang/wgsl/sem",
-    "//src/tint/utils",
-    "//src/tint/utils/containers",
-    "//src/tint/utils/diagnostic",
-    "//src/tint/utils/ice",
-    "//src/tint/utils/macros",
-    "//src/tint/utils/math",
-    "//src/tint/utils/memory",
-    "//src/tint/utils/rtti",
-    "//src/tint/utils/symbol",
-    "//src/tint/utils/text",
-    "//src/utils",
-  ],
-  copts = COPTS,
-  visibility = ["//visibility:public"],
-)
-cc_library(
-  name = "test",
-  alwayslink = True,
-  srcs = [
-    "calculate_array_length_test.cc",
-    "decompose_memory_access_test.cc",
-    "localize_struct_array_assignment_test.cc",
-    "num_workgroups_from_uniform_test.cc",
-    "pixel_local_test.cc",
-    "truncate_interstage_variables_test.cc",
-  ],
-  deps = [
-    "//src/tint/api/common",
-    "//src/tint/lang/core",
-    "//src/tint/lang/core/constant",
-    "//src/tint/lang/core/ir",
-    "//src/tint/lang/core/type",
-    "//src/tint/lang/wgsl",
-    "//src/tint/lang/wgsl/ast",
-    "//src/tint/lang/wgsl/ast/transform",
-    "//src/tint/lang/wgsl/program",
-    "//src/tint/lang/wgsl/sem",
-    "//src/tint/lang/wgsl/writer/ir_to_program",
-    "//src/tint/utils",
-    "//src/tint/utils/containers",
-    "//src/tint/utils/diagnostic",
-    "//src/tint/utils/ice",
-    "//src/tint/utils/macros",
-    "//src/tint/utils/math",
-    "//src/tint/utils/memory",
-    "//src/tint/utils/rtti",
-    "//src/tint/utils/symbol",
-    "//src/tint/utils/text",
-    "@gtest",
-    "//src/utils",
-  ] + select({
-    ":tint_build_hlsl_writer": [
-      "//src/tint/lang/hlsl/writer/ast_raise",
-    ],
-    "//conditions:default": [],
-  }) + select({
-    ":tint_build_wgsl_reader": [
-      "//src/tint/lang/wgsl/reader",
-    ],
-    "//conditions:default": [],
-  }) + select({
-    ":tint_build_wgsl_reader_and_tint_build_wgsl_writer": [
-      "//src/tint/lang/wgsl/ast/transform:test",
-    ],
-    "//conditions:default": [],
-  }) + select({
-    ":tint_build_wgsl_writer": [
-      "//src/tint/lang/wgsl/writer",
-    ],
-    "//conditions:default": [],
-  }),
-  copts = COPTS,
-  visibility = ["//visibility:public"],
-)
-
-alias(
-  name = "tint_build_hlsl_writer",
-  actual = "//src/tint:tint_build_hlsl_writer_true",
-)
-
-alias(
-  name = "tint_build_wgsl_reader",
-  actual = "//src/tint:tint_build_wgsl_reader_true",
-)
-
-alias(
-  name = "tint_build_wgsl_writer",
-  actual = "//src/tint:tint_build_wgsl_writer_true",
-)
-
-selects.config_setting_group(
-    name = "tint_build_hlsl_writer_and_tint_build_wgsl_reader_and_tint_build_wgsl_writer",
-    match_all = [
-        ":tint_build_hlsl_writer",
-        ":tint_build_wgsl_reader",
-        ":tint_build_wgsl_writer",
-    ],
-)
-selects.config_setting_group(
-    name = "tint_build_wgsl_reader_and_tint_build_wgsl_writer",
-    match_all = [
-        ":tint_build_wgsl_reader",
-        ":tint_build_wgsl_writer",
-    ],
-)
-
diff --git a/src/tint/lang/hlsl/writer/ast_raise/BUILD.cfg b/src/tint/lang/hlsl/writer/ast_raise/BUILD.cfg
deleted file mode 100644
index dd6ef8b..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/BUILD.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-    "condition": "tint_build_hlsl_writer",
-    "test": {
-        "condition": "tint_build_wgsl_reader && tint_build_wgsl_writer",
-    }
-}
diff --git a/src/tint/lang/hlsl/writer/ast_raise/BUILD.cmake b/src/tint/lang/hlsl/writer/ast_raise/BUILD.cmake
deleted file mode 100644
index 37d704d..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/BUILD.cmake
+++ /dev/null
@@ -1,154 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.cmake.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-if(TINT_BUILD_HLSL_WRITER)
-################################################################################
-# Target:    tint_lang_hlsl_writer_ast_raise
-# Kind:      lib
-# Condition: TINT_BUILD_HLSL_WRITER
-################################################################################
-tint_add_target(tint_lang_hlsl_writer_ast_raise lib
-  lang/hlsl/writer/ast_raise/calculate_array_length.cc
-  lang/hlsl/writer/ast_raise/calculate_array_length.h
-  lang/hlsl/writer/ast_raise/decompose_memory_access.cc
-  lang/hlsl/writer/ast_raise/decompose_memory_access.h
-  lang/hlsl/writer/ast_raise/localize_struct_array_assignment.cc
-  lang/hlsl/writer/ast_raise/localize_struct_array_assignment.h
-  lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.cc
-  lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.h
-  lang/hlsl/writer/ast_raise/pixel_local.cc
-  lang/hlsl/writer/ast_raise/pixel_local.h
-  lang/hlsl/writer/ast_raise/truncate_interstage_variables.cc
-  lang/hlsl/writer/ast_raise/truncate_interstage_variables.h
-)
-
-tint_target_add_dependencies(tint_lang_hlsl_writer_ast_raise lib
-  tint_api_common
-  tint_lang_core
-  tint_lang_core_constant
-  tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_program
-  tint_lang_wgsl_resolver
-  tint_lang_wgsl_sem
-  tint_utils
-  tint_utils_containers
-  tint_utils_diagnostic
-  tint_utils_ice
-  tint_utils_macros
-  tint_utils_math
-  tint_utils_memory
-  tint_utils_rtti
-  tint_utils_symbol
-  tint_utils_text
-)
-
-tint_target_add_external_dependencies(tint_lang_hlsl_writer_ast_raise lib
-  "src_utils"
-)
-
-endif(TINT_BUILD_HLSL_WRITER)
-if(TINT_BUILD_HLSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-################################################################################
-# Target:    tint_lang_hlsl_writer_ast_raise_test
-# Kind:      test
-# Condition: TINT_BUILD_HLSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER
-################################################################################
-tint_add_target(tint_lang_hlsl_writer_ast_raise_test test
-  lang/hlsl/writer/ast_raise/calculate_array_length_test.cc
-  lang/hlsl/writer/ast_raise/decompose_memory_access_test.cc
-  lang/hlsl/writer/ast_raise/localize_struct_array_assignment_test.cc
-  lang/hlsl/writer/ast_raise/num_workgroups_from_uniform_test.cc
-  lang/hlsl/writer/ast_raise/pixel_local_test.cc
-  lang/hlsl/writer/ast_raise/truncate_interstage_variables_test.cc
-)
-
-tint_target_add_dependencies(tint_lang_hlsl_writer_ast_raise_test test
-  tint_api_common
-  tint_lang_core
-  tint_lang_core_constant
-  tint_lang_core_ir
-  tint_lang_core_type
-  tint_lang_wgsl
-  tint_lang_wgsl_ast
-  tint_lang_wgsl_ast_transform
-  tint_lang_wgsl_program
-  tint_lang_wgsl_sem
-  tint_lang_wgsl_writer_ir_to_program
-  tint_utils
-  tint_utils_containers
-  tint_utils_diagnostic
-  tint_utils_ice
-  tint_utils_macros
-  tint_utils_math
-  tint_utils_memory
-  tint_utils_rtti
-  tint_utils_symbol
-  tint_utils_text
-)
-
-tint_target_add_external_dependencies(tint_lang_hlsl_writer_ast_raise_test test
-  "gtest"
-  "src_utils"
-)
-
-if(TINT_BUILD_HLSL_WRITER)
-  tint_target_add_dependencies(tint_lang_hlsl_writer_ast_raise_test test
-    tint_lang_hlsl_writer_ast_raise
-  )
-endif(TINT_BUILD_HLSL_WRITER)
-
-if(TINT_BUILD_WGSL_READER)
-  tint_target_add_dependencies(tint_lang_hlsl_writer_ast_raise_test test
-    tint_lang_wgsl_reader
-  )
-endif(TINT_BUILD_WGSL_READER)
-
-if(TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-  tint_target_add_dependencies(tint_lang_hlsl_writer_ast_raise_test test
-    tint_lang_wgsl_ast_transform_test
-  )
-endif(TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
-
-if(TINT_BUILD_WGSL_WRITER)
-  tint_target_add_dependencies(tint_lang_hlsl_writer_ast_raise_test test
-    tint_lang_wgsl_writer
-  )
-endif(TINT_BUILD_WGSL_WRITER)
-
-endif(TINT_BUILD_HLSL_WRITER AND TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
\ No newline at end of file
diff --git a/src/tint/lang/hlsl/writer/ast_raise/BUILD.gn b/src/tint/lang/hlsl/writer/ast_raise/BUILD.gn
deleted file mode 100644
index f59e28b..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/BUILD.gn
+++ /dev/null
@@ -1,141 +0,0 @@
-# Copyright 2023 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-################################################################################
-# File generated by 'tools/src/cmd/gen' using the template:
-#   tools/src/cmd/gen/build/BUILD.gn.tmpl
-#
-# To regenerate run: './tools/run gen'
-#
-#                       Do not modify this file directly
-################################################################################
-
-import("../../../../../../scripts/dawn_overrides_with_defaults.gni")
-import("../../../../../../scripts/tint_overrides_with_defaults.gni")
-
-import("${tint_src_dir}/tint.gni")
-
-if (tint_build_unittests || tint_build_benchmarks) {
-  import("//testing/test.gni")
-}
-if (tint_build_hlsl_writer) {
-  libtint_source_set("ast_raise") {
-    sources = [
-      "calculate_array_length.cc",
-      "calculate_array_length.h",
-      "decompose_memory_access.cc",
-      "decompose_memory_access.h",
-      "localize_struct_array_assignment.cc",
-      "localize_struct_array_assignment.h",
-      "num_workgroups_from_uniform.cc",
-      "num_workgroups_from_uniform.h",
-      "pixel_local.cc",
-      "pixel_local.h",
-      "truncate_interstage_variables.cc",
-      "truncate_interstage_variables.h",
-    ]
-    deps = [
-      "${dawn_root}/src/utils:utils",
-      "${tint_src_dir}/api/common",
-      "${tint_src_dir}/lang/core",
-      "${tint_src_dir}/lang/core/constant",
-      "${tint_src_dir}/lang/core/type",
-      "${tint_src_dir}/lang/wgsl",
-      "${tint_src_dir}/lang/wgsl/ast",
-      "${tint_src_dir}/lang/wgsl/ast/transform",
-      "${tint_src_dir}/lang/wgsl/program",
-      "${tint_src_dir}/lang/wgsl/resolver",
-      "${tint_src_dir}/lang/wgsl/sem",
-      "${tint_src_dir}/utils",
-      "${tint_src_dir}/utils/containers",
-      "${tint_src_dir}/utils/diagnostic",
-      "${tint_src_dir}/utils/ice",
-      "${tint_src_dir}/utils/macros",
-      "${tint_src_dir}/utils/math",
-      "${tint_src_dir}/utils/memory",
-      "${tint_src_dir}/utils/rtti",
-      "${tint_src_dir}/utils/symbol",
-      "${tint_src_dir}/utils/text",
-    ]
-  }
-}
-if (tint_build_unittests) {
-  if (tint_build_hlsl_writer && tint_build_wgsl_reader &&
-      tint_build_wgsl_writer) {
-    tint_unittests_source_set("unittests") {
-      sources = [
-        "calculate_array_length_test.cc",
-        "decompose_memory_access_test.cc",
-        "localize_struct_array_assignment_test.cc",
-        "num_workgroups_from_uniform_test.cc",
-        "pixel_local_test.cc",
-        "truncate_interstage_variables_test.cc",
-      ]
-      deps = [
-        "${dawn_root}/src/utils:utils",
-        "${tint_src_dir}:gmock_and_gtest",
-        "${tint_src_dir}/api/common",
-        "${tint_src_dir}/lang/core",
-        "${tint_src_dir}/lang/core/constant",
-        "${tint_src_dir}/lang/core/ir",
-        "${tint_src_dir}/lang/core/type",
-        "${tint_src_dir}/lang/wgsl",
-        "${tint_src_dir}/lang/wgsl/ast",
-        "${tint_src_dir}/lang/wgsl/ast/transform",
-        "${tint_src_dir}/lang/wgsl/program",
-        "${tint_src_dir}/lang/wgsl/sem",
-        "${tint_src_dir}/lang/wgsl/writer/ir_to_program",
-        "${tint_src_dir}/utils",
-        "${tint_src_dir}/utils/containers",
-        "${tint_src_dir}/utils/diagnostic",
-        "${tint_src_dir}/utils/ice",
-        "${tint_src_dir}/utils/macros",
-        "${tint_src_dir}/utils/math",
-        "${tint_src_dir}/utils/memory",
-        "${tint_src_dir}/utils/rtti",
-        "${tint_src_dir}/utils/symbol",
-        "${tint_src_dir}/utils/text",
-      ]
-
-      if (tint_build_hlsl_writer) {
-        deps += [ "${tint_src_dir}/lang/hlsl/writer/ast_raise" ]
-      }
-
-      if (tint_build_wgsl_reader) {
-        deps += [ "${tint_src_dir}/lang/wgsl/reader" ]
-      }
-
-      if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
-        deps += [ "${tint_src_dir}/lang/wgsl/ast/transform:unittests" ]
-      }
-
-      if (tint_build_wgsl_writer) {
-        deps += [ "${tint_src_dir}/lang/wgsl/writer" ]
-      }
-    }
-  }
-}
diff --git a/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.cc b/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.cc
deleted file mode 100644
index 4f37291..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.cc
+++ /dev/null
@@ -1,262 +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/hlsl/writer/ast_raise/calculate_array_length.h"
-
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/lang/core/type/reference.h"
-#include "src/tint/lang/wgsl/ast/call_statement.h"
-#include "src/tint/lang/wgsl/ast/disable_validation_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.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/lang/wgsl/sem/struct.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/math/hash.h"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::CalculateArrayLength);
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::CalculateArrayLength::BufferSizeIntrinsic);
-
-namespace tint::hlsl::writer {
-namespace {
-
-using namespace tint::core::fluent_types;     // NOLINT
-using namespace tint::core::number_suffixes;  // NOLINT
-
-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;
-}
-
-/// ArrayUsage describes a runtime array usage.
-/// It is used as a key by the array_length_by_usage map.
-struct ArrayUsage {
-    ast::BlockStatement const* const block;
-    sem::Variable const* const buffer;
-    bool operator==(const ArrayUsage& rhs) const {
-        return block == rhs.block && buffer == rhs.buffer;
-    }
-    struct Hasher {
-        inline std::size_t operator()(const ArrayUsage& u) const { return Hash(u.block, u.buffer); }
-    };
-};
-
-}  // namespace
-
-CalculateArrayLength::BufferSizeIntrinsic::BufferSizeIntrinsic(GenerationID pid, ast::NodeID nid)
-    : Base(pid, nid, tint::Empty) {}
-CalculateArrayLength::BufferSizeIntrinsic::~BufferSizeIntrinsic() = default;
-std::string CalculateArrayLength::BufferSizeIntrinsic::InternalName() const {
-    return "intrinsic_buffer_size";
-}
-
-const CalculateArrayLength::BufferSizeIntrinsic* CalculateArrayLength::BufferSizeIntrinsic::Clone(
-    ast::CloneContext& ctx) const {
-    return ctx.dst->ASTNodes().Create<CalculateArrayLength::BufferSizeIntrinsic>(
-        ctx.dst->ID(), ctx.dst->AllocateNodeID());
-}
-
-CalculateArrayLength::CalculateArrayLength() = default;
-CalculateArrayLength::~CalculateArrayLength() = default;
-
-ast::transform::Transform::ApplyResult CalculateArrayLength::Apply(const Program& src,
-                                                                   const ast::transform::DataMap&,
-                                                                   ast::transform::DataMap&) const {
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-    auto& sem = src.Sem();
-
-    // get_buffer_size_intrinsic() emits the function decorated with
-    // BufferSizeIntrinsic that is transformed by the HLSL writer into a call to
-    // [RW]ByteAddressBuffer.GetDimensions().
-    std::unordered_map<const core::type::Reference*, Symbol> buffer_size_intrinsics;
-    auto get_buffer_size_intrinsic = [&](const core::type::Reference* buffer_type) {
-        return tint::GetOrAdd(buffer_size_intrinsics, buffer_type, [&] {
-            auto name = b.Sym();
-            auto type = CreateASTTypeFor(ctx, buffer_type);
-            auto* disable_validation = b.Disable(ast::DisabledValidation::kFunctionParameter);
-            b.Func(name,
-                   Vector{
-                       b.Param("buffer",
-                               b.ty.ptr(buffer_type->AddressSpace(), type, buffer_type->Access()),
-                               Vector{disable_validation}),
-                       b.Param("result", b.ty.ptr<function, u32>()),
-                   },
-                   b.ty.void_(), nullptr,
-                   Vector{
-                       b.ASTNodes().Create<BufferSizeIntrinsic>(b.ID(), b.AllocateNodeID()),
-                   });
-
-            return name;
-        });
-    };
-
-    std::unordered_map<ArrayUsage, Symbol, ArrayUsage::Hasher> array_length_by_usage;
-
-    // Find all the arrayLength() calls...
-    for (auto* node : src.ASTNodes().Objects()) {
-        if (auto* call_expr = node->As<ast::CallExpression>()) {
-            auto* call = sem.Get(call_expr)->UnwrapMaterialize()->As<sem::Call>();
-            if (auto* builtin = call->Target()->As<sem::BuiltinFn>()) {
-                if (builtin->Fn() == wgsl::BuiltinFn::kArrayLength) {
-                    // We're dealing with an arrayLength() call
-
-                    if (auto* call_stmt = call->Stmt()->Declaration()->As<ast::CallStatement>()) {
-                        if (call_stmt->expr == call_expr) {
-                            // arrayLength() is used as a statement.
-                            // The argument expression must be side-effect free, so just drop the
-                            // statement.
-                            RemoveStatement(ctx, call_stmt);
-                            continue;
-                        }
-                    }
-
-                    // A runtime-sized array can only appear as the store type of a variable, or the
-                    // last element of a structure (which cannot itself be nested). Given that we
-                    // require SimplifyPointers, we can assume that the arrayLength() call has one
-                    // of two forms:
-                    //   arrayLength(&struct_var.array_member)
-                    //   arrayLength(&array_var)
-                    auto* arg = call_expr->args[0];
-                    auto* address_of = arg->As<ast::UnaryOpExpression>();
-                    if (DAWN_UNLIKELY(!address_of || address_of->op != core::UnaryOp::kAddressOf)) {
-                        TINT_ICE()
-                            << "arrayLength() expected address-of, got " << arg->TypeInfo().name;
-                    }
-                    auto* storage_buffer_expr = address_of->expr;
-                    if (auto* accessor = storage_buffer_expr->As<ast::MemberAccessorExpression>()) {
-                        storage_buffer_expr = accessor->object;
-                    }
-                    auto* storage_buffer_sem = sem.Get<sem::VariableUser>(storage_buffer_expr);
-                    if (DAWN_UNLIKELY(!storage_buffer_sem)) {
-                        TINT_ICE() << "expected form of arrayLength argument to be &array_var or "
-                                      "&struct_var.array_member";
-                    }
-                    if (DAWN_UNLIKELY(storage_buffer_sem->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_var = storage_buffer_sem->Variable();
-                    auto* storage_buffer_type =
-                        storage_buffer_sem->Type()->As<core::type::Reference>();
-
-                    // Generate BufferSizeIntrinsic for this storage type if we haven't already
-                    auto buffer_size = get_buffer_size_intrinsic(storage_buffer_type);
-
-                    // Find the current statement block
-                    auto* block = call->Stmt()->Block()->Declaration();
-
-                    auto array_length =
-                        tint::GetOrAdd(array_length_by_usage, {block, storage_buffer_var}, [&] {
-                            // First time this array length is used for this block.
-                            // Let's calculate it.
-
-                            // Construct the variable that'll hold the result of
-                            // RWByteAddressBuffer.GetDimensions()
-                            auto* buffer_size_result =
-                                b.Decl(b.Var(b.Sym(), b.ty.u32(), b.Expr(0_u)));
-
-                            // Call storage_buffer.GetDimensions(&buffer_size_result)
-                            auto* call_get_dims = b.CallStmt(b.Call(
-                                // BufferSizeIntrinsic(X, ARGS...) is
-                                // translated to:
-                                //  X.GetDimensions(ARGS..) by the writer
-                                buffer_size, b.AddressOf(ctx.Clone(storage_buffer_expr)),
-                                b.AddressOf(b.Expr(buffer_size_result->variable->name->symbol))));
-
-                            // Calculate actual array length
-                            //                total_storage_buffer_size - array_offset
-                            // array_length = ----------------------------------------
-                            //                             array_stride
-                            auto name = b.Sym();
-                            const ast::Expression* total_size =
-                                b.Expr(buffer_size_result->variable);
-
-                            const core::type::Array* array_type = Switch(
-                                storage_buffer_type->StoreType(),
-                                [&](const core::type::Struct* str) {
-                                    // The variable is a struct, so subtract the byte offset of
-                                    // the array member.
-                                    auto* array_member_sem = str->Members().Back();
-                                    total_size = b.Sub(total_size, u32(array_member_sem->Offset()));
-                                    return array_member_sem->Type()->As<core::type::Array>();
-                                },
-                                [&](const core::type::Array* arr) { return arr; });
-
-                            if (DAWN_UNLIKELY(!array_type)) {
-                                TINT_ICE() << "expected form of arrayLength argument to be "
-                                              "&array_var or &struct_var.array_member";
-                            }
-
-                            uint32_t array_stride = array_type->Size();
-                            auto* array_length_var = b.Decl(
-                                b.Let(name, b.ty.u32(), b.Div(total_size, u32(array_stride))));
-
-                            // Insert the array length calculations at the top of the block
-                            ctx.InsertBefore(block->statements, block->statements[0],
-                                             buffer_size_result);
-                            ctx.InsertBefore(block->statements, block->statements[0],
-                                             call_get_dims);
-                            ctx.InsertBefore(block->statements, block->statements[0],
-                                             array_length_var);
-                            return name;
-                        });
-
-                    // Replace the call to arrayLength() with the array length variable
-                    ctx.Replace(call_expr, b.Expr(array_length));
-                }
-            }
-        }
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.h b/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.h
deleted file mode 100644
index fc822f4..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.h
+++ /dev/null
@@ -1,79 +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_HLSL_WRITER_AST_RAISE_CALCULATE_ARRAY_LENGTH_H_
-#define SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_CALCULATE_ARRAY_LENGTH_H_
-
-#include <string>
-
-#include "src/tint/lang/wgsl/ast/internal_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::hlsl::writer {
-
-/// CalculateArrayLength is a transform used to replace calls to arrayLength()
-/// with a value calculated from the size of the storage buffer.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * SimplifyPointers
-class CalculateArrayLength final
-    : public Castable<CalculateArrayLength, ast::transform::Transform> {
-  public:
-    /// BufferSizeIntrinsic is an InternalAttribute that's applied to intrinsic
-    /// functions used to obtain the runtime size of a storage buffer.
-    class BufferSizeIntrinsic final : public Castable<BufferSizeIntrinsic, ast::InternalAttribute> {
-      public:
-        /// Constructor
-        /// @param generation_id the identifier of the program that owns this node
-        /// @param nid the unique node identifier
-        BufferSizeIntrinsic(GenerationID generation_id, ast::NodeID nid);
-        /// Destructor
-        ~BufferSizeIntrinsic() override;
-
-        /// @return "buffer_size"
-        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 BufferSizeIntrinsic* Clone(ast::CloneContext& ctx) const override;
-    };
-
-    /// Constructor
-    CalculateArrayLength();
-    /// Destructor
-    ~CalculateArrayLength() override;
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-};
-
-}  // namespace tint::hlsl::writer
-
-#endif  // SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_CALCULATE_ARRAY_LENGTH_H_
diff --git a/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length_test.cc b/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length_test.cc
deleted file mode 100644
index 60e769b..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/calculate_array_length_test.cc
+++ /dev/null
@@ -1,607 +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/hlsl/writer/ast_raise/calculate_array_length.h"
-
-#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::hlsl::writer {
-namespace {
-
-using CalculateArrayLengthTest = ast::transform::TransformTest;
-using Unshadow = ast::transform::Unshadow;
-using SimplifyPointers = ast::transform::SimplifyPointers;
-
-TEST_F(CalculateArrayLengthTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<CalculateArrayLength>(src));
-}
-
-TEST_F(CalculateArrayLengthTest, 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() {
-}
-)";
-
-    EXPECT_FALSE(ShouldRun<CalculateArrayLength>(src));
-}
-
-TEST_F(CalculateArrayLengthTest, 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);
-}
-)";
-
-    EXPECT_TRUE(ShouldRun<CalculateArrayLength>(src));
-}
-
-TEST_F(CalculateArrayLengthTest, BasicArray) {
-    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"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, array<i32>, read>, result : ptr<function, u32>)
-
-@group(0) @binding(0) var<storage, read> sb : array<i32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(sb), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = (tint_symbol_1 / 4u);
-  var len : u32 = tint_symbol_2;
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, 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"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read>, result : ptr<function, u32>)
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(sb), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
-  var len : u32 = tint_symbol_2;
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, 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"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read>, result : ptr<function, u32>)
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(sb), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
-  var len : u32 = tint_symbol_2;
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, ArrayOfStruct) {
-    auto* src = R"(
-struct S {
-  f : f32,
-}
-
-@group(0) @binding(0) var<storage, read> arr : array<S>;
-
-@compute @workgroup_size(1)
-fn main() {
-  let len = arrayLength(&arr);
-}
-)";
-    auto* expect = R"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, array<S>, read>, result : ptr<function, u32>)
-
-struct S {
-  f : f32,
-}
-
-@group(0) @binding(0) var<storage, read> arr : array<S>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(arr), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = (tint_symbol_1 / 4u);
-  let len = tint_symbol_2;
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, ArrayOfArrayOfStruct) {
-    auto* src = R"(
-struct S {
-  f : f32,
-}
-
-@group(0) @binding(0) var<storage, read> arr : array<array<S, 4>>;
-
-@compute @workgroup_size(1)
-fn main() {
-  let len = arrayLength(&arr);
-}
-)";
-    auto* expect = R"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, array<array<S, 4u>>, read>, result : ptr<function, u32>)
-
-struct S {
-  f : f32,
-}
-
-@group(0) @binding(0) var<storage, read> arr : array<array<S, 4>>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(arr), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = (tint_symbol_1 / 16u);
-  let len = tint_symbol_2;
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, InSameBlock) {
-    auto* src = R"(
-@group(0) @binding(0) var<storage, read> sb : array<i32>;;
-
-@compute @workgroup_size(1)
-fn main() {
-  var a : u32 = arrayLength(&sb);
-  var b : u32 = arrayLength(&sb);
-  var c : u32 = arrayLength(&sb);
-}
-)";
-
-    auto* expect = R"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, array<i32>, read>, result : ptr<function, u32>)
-
-@group(0) @binding(0) var<storage, read> sb : array<i32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(sb), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = (tint_symbol_1 / 4u);
-  var a : u32 = tint_symbol_2;
-  var b : u32 = tint_symbol_2;
-  var c : u32 = tint_symbol_2;
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, InSameBlock_Struct) {
-    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 a : u32 = arrayLength(&sb.arr);
-  var b : u32 = arrayLength(&sb.arr);
-  var c : u32 = arrayLength(&sb.arr);
-}
-)";
-
-    auto* expect = R"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read>, result : ptr<function, u32>)
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(sb), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
-  var a : u32 = tint_symbol_2;
-  var b : u32 = tint_symbol_2;
-  var c : u32 = tint_symbol_2;
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, Nested) {
-    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() {
-  if (true) {
-    var len : u32 = arrayLength(&sb.arr);
-  } else {
-    if (true) {
-      var len : u32 = arrayLength(&sb.arr);
-    }
-  }
-}
-)";
-
-    auto* expect = R"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read>, result : ptr<function, u32>)
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  if (true) {
-    var tint_symbol_1 : u32 = 0u;
-    tint_symbol(&(sb), &(tint_symbol_1));
-    let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
-    var len : u32 = tint_symbol_2;
-  } else {
-    if (true) {
-      var tint_symbol_3 : u32 = 0u;
-      tint_symbol(&(sb), &(tint_symbol_3));
-      let tint_symbol_4 : u32 = ((tint_symbol_3 - 4u) / 4u);
-      var len : u32 = tint_symbol_4;
-    }
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, MultipleStorageBuffers) {
-    auto* src = R"(
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-};
-
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-};
-
-@group(0) @binding(0) var<storage, read> sb1 : SB1;
-
-@group(0) @binding(1) var<storage, read> sb2 : SB2;
-
-@group(0) @binding(2) var<storage, read> sb3 : array<i32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len1 : u32 = arrayLength(&(sb1.arr1));
-  var len2 : u32 = arrayLength(&(sb2.arr2));
-  var len3 : u32 = arrayLength(&sb3);
-  var x : u32 = (len1 + len2 + len3);
-}
-)";
-
-    auto* expect = R"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB1, read>, result : ptr<function, u32>)
-
-@internal(intrinsic_buffer_size)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB2, read>, result : ptr<function, u32>)
-
-@internal(intrinsic_buffer_size)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, array<i32>, read>, result : ptr<function, u32>)
-
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-}
-
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-}
-
-@group(0) @binding(0) var<storage, read> sb1 : SB1;
-
-@group(0) @binding(1) var<storage, read> sb2 : SB2;
-
-@group(0) @binding(2) var<storage, read> sb3 : array<i32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(sb1), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
-  var tint_symbol_4 : u32 = 0u;
-  tint_symbol_3(&(sb2), &(tint_symbol_4));
-  let tint_symbol_5 : u32 = ((tint_symbol_4 - 16u) / 16u);
-  var tint_symbol_7 : u32 = 0u;
-  tint_symbol_6(&(sb3), &(tint_symbol_7));
-  let tint_symbol_8 : u32 = (tint_symbol_7 / 4u);
-  var len1 : u32 = tint_symbol_2;
-  var len2 : u32 = tint_symbol_5;
-  var len3 : u32 = tint_symbol_8;
-  var x : u32 = ((len1 + len2) + len3);
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, Shadowing) {
-    auto* src = R"(
-struct SB {
-  x : i32,
-  arr : array<i32>,
-};
-
-@group(0) @binding(0) var<storage, read> a : SB;
-@group(0) @binding(1) var<storage, read> b : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  let x = &a;
-  var a : u32 = arrayLength(&a.arr);
-  {
-    var b : u32 = arrayLength(&((*x).arr));
-  }
-}
-)";
-
-    auto* expect =
-        R"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB, read>, result : ptr<function, u32>)
-
-struct SB {
-  x : i32,
-  arr : array<i32>,
-}
-
-@group(0) @binding(0) var<storage, read> a : SB;
-
-@group(0) @binding(1) var<storage, read> b : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(a), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
-  var a_1 : u32 = tint_symbol_2;
-  {
-    var tint_symbol_3 : u32 = 0u;
-    tint_symbol(&(a), &(tint_symbol_3));
-    let tint_symbol_4 : u32 = ((tint_symbol_3 - 4u) / 4u);
-    var b_1 : u32 = tint_symbol_4;
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(CalculateArrayLengthTest, OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var len1 : u32 = arrayLength(&(sb1.arr1));
-  var len2 : u32 = arrayLength(&(sb2.arr2));
-  var len3 : u32 = arrayLength(&sb3);
-  var x : u32 = (len1 + len2 + len3);
-}
-
-@group(0) @binding(0) var<storage, read> sb1 : SB1;
-
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-};
-
-@group(0) @binding(1) var<storage, read> sb2 : SB2;
-
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-};
-
-@group(0) @binding(2) var<storage, read> sb3 : array<i32>;
-)";
-
-    auto* expect = R"(
-@internal(intrinsic_buffer_size)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB1, read>, result : ptr<function, u32>)
-
-@internal(intrinsic_buffer_size)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<storage, SB2, read>, result : ptr<function, u32>)
-
-@internal(intrinsic_buffer_size)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<storage, array<i32>, read>, result : ptr<function, u32>)
-
-@compute @workgroup_size(1)
-fn main() {
-  var tint_symbol_1 : u32 = 0u;
-  tint_symbol(&(sb1), &(tint_symbol_1));
-  let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
-  var tint_symbol_4 : u32 = 0u;
-  tint_symbol_3(&(sb2), &(tint_symbol_4));
-  let tint_symbol_5 : u32 = ((tint_symbol_4 - 16u) / 16u);
-  var tint_symbol_7 : u32 = 0u;
-  tint_symbol_6(&(sb3), &(tint_symbol_7));
-  let tint_symbol_8 : u32 = (tint_symbol_7 / 4u);
-  var len1 : u32 = tint_symbol_2;
-  var len2 : u32 = tint_symbol_5;
-  var len3 : u32 = tint_symbol_8;
-  var x : u32 = ((len1 + len2) + len3);
-}
-
-@group(0) @binding(0) var<storage, read> sb1 : SB1;
-
-struct SB1 {
-  x : i32,
-  arr1 : array<i32>,
-}
-
-@group(0) @binding(1) var<storage, read> sb2 : SB2;
-
-struct SB2 {
-  x : i32,
-  arr2 : array<vec4<f32>>,
-}
-
-@group(0) @binding(2) var<storage, read> sb3 : array<i32>;
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, CalculateArrayLength>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.cc b/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.cc
deleted file mode 100644
index 7059698..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.cc
+++ /dev/null
@@ -1,1002 +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/hlsl/writer/ast_raise/decompose_memory_access.h"
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/core/type/array.h"
-#include "src/tint/lang/core/type/atomic.h"
-#include "src/tint/lang/core/type/reference.h"
-#include "src/tint/lang/core/unary_op.h"
-#include "src/tint/lang/wgsl/ast/assignment_statement.h"
-#include "src/tint/lang/wgsl/ast/call_statement.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/call.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/struct.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/containers/map.h"
-#include "src/tint/utils/math/hash.h"
-#include "src/tint/utils/memory/block_allocator.h"
-#include "src/tint/utils/rtti/switch.h"
-#include "src/tint/utils/text/string_stream.h"
-
-using namespace tint::core::number_suffixes;  // NOLINT
-using namespace tint::core::fluent_types;     // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::DecomposeMemoryAccess);
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::DecomposeMemoryAccess::Intrinsic);
-
-namespace tint::hlsl::writer {
-
-namespace {
-
-bool ShouldRun(const Program& program) {
-    for (auto* decl : program.AST().GlobalDeclarations()) {
-        if (auto* var = program.Sem().Get<sem::Variable>(decl)) {
-            if (var->AddressSpace() == core::AddressSpace::kStorage ||
-                var->AddressSpace() == core::AddressSpace::kUniform) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-/// Offset is a simple Expression builder interface, used to build byte
-/// offsets for storage and uniform buffer accesses.
-struct Offset : Castable<Offset> {
-    /// @returns builds and returns the Expression in `ctx.dst`
-    virtual const ast::Expression* Build(program::CloneContext& ctx) const = 0;
-};
-
-/// OffsetExpr is an implementation of Offset that clones and casts the given
-/// expression to `u32`.
-struct OffsetExpr : Offset {
-    const ast::Expression* const expr = nullptr;
-
-    explicit OffsetExpr(const ast::Expression* e) : expr(e) {}
-
-    const ast::Expression* Build(program::CloneContext& ctx) const override {
-        auto* type = ctx.src->Sem().GetVal(expr)->Type()->UnwrapRef();
-        auto* res = ctx.Clone(expr);
-        if (!type->Is<core::type::U32>()) {
-            res = ctx.dst->Call<u32>(res);
-        }
-        return res;
-    }
-};
-
-/// OffsetLiteral is an implementation of Offset that constructs a u32 literal
-/// value.
-struct OffsetLiteral final : Castable<OffsetLiteral, Offset> {
-    uint32_t const literal = 0;
-
-    explicit OffsetLiteral(uint32_t lit) : literal(lit) {}
-
-    const ast::Expression* Build(program::CloneContext& ctx) const override {
-        return ctx.dst->Expr(u32(literal));
-    }
-};
-
-/// OffsetBinOp is an implementation of Offset that constructs a binary-op of
-/// two Offsets.
-struct OffsetBinOp : Offset {
-    core::BinaryOp op;
-    Offset const* lhs = nullptr;
-    Offset const* rhs = nullptr;
-
-    const ast::Expression* Build(program::CloneContext& ctx) const override {
-        return ctx.dst->create<ast::BinaryExpression>(op, lhs->Build(ctx), rhs->Build(ctx));
-    }
-};
-
-/// LoadStoreKey is the unordered map key to a load or store intrinsic.
-struct LoadStoreKey {
-    core::type::Type const* el_ty = nullptr;  // element type
-    Symbol const buffer;                      // buffer name
-    bool operator==(const LoadStoreKey& rhs) const {
-        return el_ty == rhs.el_ty && buffer == rhs.buffer;
-    }
-    struct Hasher {
-        inline std::size_t operator()(const LoadStoreKey& u) const {
-            return Hash(u.el_ty, u.buffer);
-        }
-    };
-};
-
-/// AtomicKey is the unordered map key to an atomic intrinsic.
-struct AtomicKey {
-    core::type::Type const* el_ty = nullptr;  // element type
-    wgsl::BuiltinFn const op;                 // atomic op
-    Symbol const buffer;                      // buffer name
-    bool operator==(const AtomicKey& rhs) const {
-        return el_ty == rhs.el_ty && op == rhs.op && buffer == rhs.buffer;
-    }
-    struct Hasher {
-        inline std::size_t operator()(const AtomicKey& u) const {
-            return Hash(u.el_ty, u.op, u.buffer);
-        }
-    };
-};
-
-bool IntrinsicDataTypeFor(const core::type::Type* ty,
-                          DecomposeMemoryAccess::Intrinsic::DataType& out) {
-    if (ty->Is<core::type::I32>()) {
-        out = DecomposeMemoryAccess::Intrinsic::DataType::kI32;
-        return true;
-    }
-    if (ty->Is<core::type::U32>()) {
-        out = DecomposeMemoryAccess::Intrinsic::DataType::kU32;
-        return true;
-    }
-    if (ty->Is<core::type::F32>()) {
-        out = DecomposeMemoryAccess::Intrinsic::DataType::kF32;
-        return true;
-    }
-    if (ty->Is<core::type::F16>()) {
-        out = DecomposeMemoryAccess::Intrinsic::DataType::kF16;
-        return true;
-    }
-    if (auto* vec = ty->As<core::type::Vector>()) {
-        switch (vec->Width()) {
-            case 2:
-                if (vec->Type()->Is<core::type::I32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec2I32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::U32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec2U32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::F32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec2F32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::F16>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec2F16;
-                    return true;
-                }
-                break;
-            case 3:
-                if (vec->Type()->Is<core::type::I32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec3I32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::U32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec3U32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::F32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec3F32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::F16>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec3F16;
-                    return true;
-                }
-                break;
-            case 4:
-                if (vec->Type()->Is<core::type::I32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec4I32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::U32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec4U32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::F32>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec4F32;
-                    return true;
-                }
-                if (vec->Type()->Is<core::type::F16>()) {
-                    out = DecomposeMemoryAccess::Intrinsic::DataType::kVec4F16;
-                    return true;
-                }
-                break;
-        }
-        return false;
-    }
-
-    return false;
-}
-
-/// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied to a stub function to
-/// load the type @p ty from the uniform or storage buffer with name @p buffer.
-DecomposeMemoryAccess::Intrinsic* IntrinsicLoadFor(ast::Builder* builder,
-                                                   const core::type::Type* ty,
-                                                   core::AddressSpace address_space,
-                                                   const Symbol& buffer) {
-    DecomposeMemoryAccess::Intrinsic::DataType type;
-    if (!IntrinsicDataTypeFor(ty, type)) {
-        return nullptr;
-    }
-    return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
-        builder->ID(), builder->AllocateNodeID(), DecomposeMemoryAccess::Intrinsic::Op::kLoad, type,
-        address_space, builder->Expr(buffer));
-}
-
-/// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied to a stub function to
-/// store the type @p ty to the storage buffer with name @p buffer.
-DecomposeMemoryAccess::Intrinsic* IntrinsicStoreFor(ast::Builder* builder,
-                                                    const core::type::Type* ty,
-                                                    const Symbol& buffer) {
-    DecomposeMemoryAccess::Intrinsic::DataType type;
-    if (!IntrinsicDataTypeFor(ty, type)) {
-        return nullptr;
-    }
-    return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
-        builder->ID(), builder->AllocateNodeID(), DecomposeMemoryAccess::Intrinsic::Op::kStore,
-        type, core::AddressSpace::kStorage, builder->Expr(buffer));
-}
-
-/// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied to a stub function for
-/// the atomic op and the type @p ty.
-DecomposeMemoryAccess::Intrinsic* IntrinsicAtomicFor(ast::Builder* builder,
-                                                     wgsl::BuiltinFn ity,
-                                                     const core::type::Type* ty,
-                                                     const Symbol& buffer) {
-    auto op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicLoad;
-    switch (ity) {
-        case wgsl::BuiltinFn::kAtomicLoad:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicLoad;
-            break;
-        case wgsl::BuiltinFn::kAtomicStore:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicStore;
-            break;
-        case wgsl::BuiltinFn::kAtomicAdd:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicAdd;
-            break;
-        case wgsl::BuiltinFn::kAtomicSub:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicSub;
-            break;
-        case wgsl::BuiltinFn::kAtomicMax:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicMax;
-            break;
-        case wgsl::BuiltinFn::kAtomicMin:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicMin;
-            break;
-        case wgsl::BuiltinFn::kAtomicAnd:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicAnd;
-            break;
-        case wgsl::BuiltinFn::kAtomicOr:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicOr;
-            break;
-        case wgsl::BuiltinFn::kAtomicXor:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicXor;
-            break;
-        case wgsl::BuiltinFn::kAtomicExchange:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicExchange;
-            break;
-        case wgsl::BuiltinFn::kAtomicCompareExchangeWeak:
-            op = DecomposeMemoryAccess::Intrinsic::Op::kAtomicCompareExchangeWeak;
-            break;
-        default:
-            TINT_ICE() << "invalid IntrinsicType for DecomposeMemoryAccess::Intrinsic: "
-                       << ty->TypeInfo().name;
-    }
-
-    DecomposeMemoryAccess::Intrinsic::DataType type;
-    if (!IntrinsicDataTypeFor(ty, type)) {
-        return nullptr;
-    }
-    return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
-        builder->ID(), builder->AllocateNodeID(), op, type, core::AddressSpace::kStorage,
-        builder->Expr(buffer));
-}
-
-/// BufferAccess describes a single storage or uniform buffer access
-struct BufferAccess {
-    sem::GlobalVariable const* var = nullptr;       // Storage or uniform buffer variable
-    Offset const* offset = nullptr;                 // The byte offset on var
-    core::type::Type const* type = nullptr;         // The type of the access
-    explicit operator bool() const { return var != nullptr; }  // Returns true if valid
-};
-
-/// Store describes a single storage or uniform buffer write
-struct Store {
-    const ast::AssignmentStatement* assignment;  // The AST assignment statement
-    BufferAccess target;                         // The target for the write
-};
-
-}  // namespace
-
-/// PIMPL state for the transform
-struct DecomposeMemoryAccess::State {
-    /// The clone context
-    program::CloneContext& ctx;
-    /// Alias to `*ctx.dst`
-    ast::Builder& b;
-    /// Map of AST expression to storage or uniform buffer access
-    /// This map has entries added when encountered, and removed when outer
-    /// expressions chain the access.
-    /// Subset of #expression_order, as expressions are not removed from
-    /// #expression_order.
-    std::unordered_map<const ast::Expression*, BufferAccess> accesses;
-    /// The visited order of AST expressions (superset of #accesses)
-    std::vector<const ast::Expression*> expression_order;
-    /// [buffer-type, element-type] -> load function name
-    std::unordered_map<LoadStoreKey, Symbol, LoadStoreKey::Hasher> load_funcs;
-    /// [buffer-type, element-type] -> store function name
-    std::unordered_map<LoadStoreKey, Symbol, LoadStoreKey::Hasher> store_funcs;
-    /// [buffer-type, element-type, atomic-op] -> load function name
-    std::unordered_map<AtomicKey, Symbol, AtomicKey::Hasher> atomic_funcs;
-    /// List of storage or uniform buffer writes
-    std::vector<Store> stores;
-    /// Allocations for offsets
-    BlockAllocator<Offset> offsets_;
-
-    /// Constructor
-    /// @param context the program::CloneContext
-    explicit State(program::CloneContext& context) : ctx(context), b(*ctx.dst) {}
-
-    /// @param offset the offset value to wrap in an Offset
-    /// @returns an Offset for the given literal value
-    const Offset* ToOffset(uint32_t offset) { return offsets_.Create<OffsetLiteral>(offset); }
-
-    /// @param expr the expression to convert to an Offset
-    /// @returns an Offset for the given Expression
-    const Offset* ToOffset(const ast::Expression* expr) {
-        if (auto* lit = expr->As<ast::IntLiteralExpression>()) {
-            if (lit->value >= 0) {
-                return offsets_.Create<OffsetLiteral>(static_cast<uint32_t>(lit->value));
-            }
-        }
-        return offsets_.Create<OffsetExpr>(expr);
-    }
-
-    /// @param offset the Offset that is returned
-    /// @returns the given offset (pass-through)
-    const Offset* ToOffset(const Offset* offset) { return offset; }
-
-    /// @param lhs_ the left-hand side of the add expression
-    /// @param rhs_ the right-hand side of the add expression
-    /// @return an Offset that is a sum of lhs and rhs, performing basic constant
-    /// folding if possible
-    template <typename LHS, typename RHS>
-    const Offset* Add(LHS&& lhs_, RHS&& rhs_) {
-        auto* lhs = ToOffset(std::forward<LHS>(lhs_));
-        auto* rhs = ToOffset(std::forward<RHS>(rhs_));
-        auto* lhs_lit = tint::As<OffsetLiteral>(lhs);
-        auto* rhs_lit = tint::As<OffsetLiteral>(rhs);
-        if (lhs_lit && lhs_lit->literal == 0) {
-            return rhs;
-        }
-        if (rhs_lit && rhs_lit->literal == 0) {
-            return lhs;
-        }
-        if (lhs_lit && rhs_lit) {
-            if (static_cast<uint64_t>(lhs_lit->literal) + static_cast<uint64_t>(rhs_lit->literal) <=
-                0xffffffff) {
-                return offsets_.Create<OffsetLiteral>(lhs_lit->literal + rhs_lit->literal);
-            }
-        }
-        auto* out = offsets_.Create<OffsetBinOp>();
-        out->op = core::BinaryOp::kAdd;
-        out->lhs = lhs;
-        out->rhs = rhs;
-        return out;
-    }
-
-    /// @param lhs_ the left-hand side of the multiply expression
-    /// @param rhs_ the right-hand side of the multiply expression
-    /// @return an Offset that is the multiplication of lhs and rhs, performing
-    /// basic constant folding if possible
-    template <typename LHS, typename RHS>
-    const Offset* Mul(LHS&& lhs_, RHS&& rhs_) {
-        auto* lhs = ToOffset(std::forward<LHS>(lhs_));
-        auto* rhs = ToOffset(std::forward<RHS>(rhs_));
-        auto* lhs_lit = tint::As<OffsetLiteral>(lhs);
-        auto* rhs_lit = tint::As<OffsetLiteral>(rhs);
-        if (lhs_lit && lhs_lit->literal == 0) {
-            return offsets_.Create<OffsetLiteral>(0u);
-        }
-        if (rhs_lit && rhs_lit->literal == 0) {
-            return offsets_.Create<OffsetLiteral>(0u);
-        }
-        if (lhs_lit && lhs_lit->literal == 1) {
-            return rhs;
-        }
-        if (rhs_lit && rhs_lit->literal == 1) {
-            return lhs;
-        }
-        if (lhs_lit && rhs_lit) {
-            return offsets_.Create<OffsetLiteral>(lhs_lit->literal * rhs_lit->literal);
-        }
-        auto* out = offsets_.Create<OffsetBinOp>();
-        out->op = core::BinaryOp::kMultiply;
-        out->lhs = lhs;
-        out->rhs = rhs;
-        return out;
-    }
-
-    /// AddAccess() adds the `expr -> access` map item to #accesses, and `expr`
-    /// to #expression_order.
-    /// @param expr the expression that performs the access
-    /// @param access the access
-    void AddAccess(const ast::Expression* expr, const BufferAccess& access) {
-        TINT_ASSERT(access.type);
-        accesses.emplace(expr, access);
-        expression_order.emplace_back(expr);
-    }
-
-    /// TakeAccess() removes the `node` item from #accesses (if it exists),
-    /// returning the BufferAccess. If #accesses does not hold an item for
-    /// `node`, an invalid BufferAccess is returned.
-    /// @param node the expression that performed an access
-    /// @return the BufferAccess for the given expression
-    BufferAccess TakeAccess(const ast::Expression* node) {
-        auto lhs_it = accesses.find(node);
-        if (lhs_it == accesses.end()) {
-            return {};
-        }
-        auto access = lhs_it->second;
-        accesses.erase(node);
-        return access;
-    }
-
-    /// LoadFunc() returns a symbol to an intrinsic function that loads an element of type @p el_ty
-    /// from a storage or uniform buffer with name @p buffer.
-    /// The emitted function has the signature:
-    ///   `fn load(offset : u32) -> el_ty`
-    /// @param el_ty the storage or uniform buffer element type
-    /// @param address_space either kUniform or kStorage
-    /// @param buffer the symbol of the storage or uniform buffer variable, owned by the target
-    /// ProgramBuilder.
-    /// @return the name of the function that performs the load
-    Symbol LoadFunc(const core::type::Type* el_ty,
-                    core::AddressSpace address_space,
-                    const Symbol& buffer) {
-        return tint::GetOrAdd(load_funcs, LoadStoreKey{el_ty, buffer}, [&] {
-            Vector params{b.Param("offset", b.ty.u32())};
-
-            auto name = b.Symbols().New(buffer.Name() + "_load");
-
-            if (auto* intrinsic = IntrinsicLoadFor(ctx.dst, el_ty, address_space, buffer)) {
-                auto el_ast_ty = CreateASTTypeFor(ctx, el_ty);
-                b.Func(name, params, el_ast_ty, nullptr,
-                       Vector{
-                           intrinsic,
-                           b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
-                       });
-            } else if (auto* arr_ty = el_ty->As<core::type::Array>()) {
-                // fn load_func(buffer : buf_ty, offset : u32) -> array<T, N> {
-                //   var arr : array<T, N>;
-                //   for (var i = 0u; i < array_count; i = i + 1) {
-                //     arr[i] = el_load_func(buffer, offset + i * array_stride)
-                //   }
-                //   return arr;
-                // }
-                auto load = LoadFunc(arr_ty->ElemType()->UnwrapRef(), address_space, buffer);
-                auto* arr = b.Var(b.Symbols().New("arr"), CreateASTTypeFor(ctx, arr_ty));
-                auto* i = b.Var(b.Symbols().New("i"), b.Expr(0_u));
-                auto* for_init = b.Decl(i);
-                auto arr_cnt = arr_ty->ConstantCount();
-                if (DAWN_UNLIKELY(!arr_cnt)) {
-                    // Non-constant counts should not be possible:
-                    // * Override-expression counts can only be applied to workgroup arrays, and
-                    //   this method only handles storage and uniform.
-                    // * Runtime-sized arrays are not loadable.
-                    TINT_ICE() << "unexpected non-constant array count";
-                }
-                auto* for_cond = b.create<ast::BinaryExpression>(
-                    core::BinaryOp::kLessThan, b.Expr(i), b.Expr(u32(arr_cnt.value())));
-                auto* for_cont = b.Assign(i, b.Add(i, 1_u));
-                auto* arr_el = b.IndexAccessor(arr, i);
-                auto* el_offset = b.Add(b.Expr("offset"), b.Mul(i, u32(arr_ty->Stride())));
-                auto* el_val = b.Call(load, el_offset);
-                auto* for_loop =
-                    b.For(for_init, for_cond, for_cont, b.Block(b.Assign(arr_el, el_val)));
-
-                b.Func(name, params, CreateASTTypeFor(ctx, arr_ty),
-                       Vector{
-                           b.Decl(arr),
-                           for_loop,
-                           b.Return(arr),
-                       });
-            } else {
-                Vector<const ast::Expression*, 8> values;
-                if (auto* mat_ty = el_ty->As<core::type::Matrix>()) {
-                    auto* vec_ty = mat_ty->ColumnType();
-                    Symbol load = LoadFunc(vec_ty, address_space, buffer);
-                    for (uint32_t i = 0; i < mat_ty->Columns(); i++) {
-                        auto* offset = b.Add("offset", u32(i * mat_ty->ColumnStride()));
-                        values.Push(b.Call(load, offset));
-                    }
-                } else if (auto* str = el_ty->As<core::type::Struct>()) {
-                    for (auto* member : str->Members()) {
-                        auto* offset = b.Add("offset", u32(member->Offset()));
-                        Symbol load = LoadFunc(member->Type()->UnwrapRef(), address_space, buffer);
-                        values.Push(b.Call(load, offset));
-                    }
-                }
-                b.Func(name, params, CreateASTTypeFor(ctx, el_ty),
-                       Vector{
-                           b.Return(b.Call(CreateASTTypeFor(ctx, el_ty), values)),
-                       });
-            }
-            return name;
-        });
-    }
-
-    /// StoreFunc() returns a symbol to an intrinsic function that stores an element of type @p
-    /// el_ty to the storage buffer @p buffer. The function has the signature:
-    ///   `fn store(offset : u32, value : el_ty)`
-    /// @param el_ty the storage buffer element type
-    /// @param buffer the symbol of the storage buffer variable, owned by the target ProgramBuilder.
-    /// @return the name of the function that performs the store
-    Symbol StoreFunc(const core::type::Type* el_ty, const Symbol& buffer) {
-        return tint::GetOrAdd(store_funcs, LoadStoreKey{el_ty, buffer}, [&] {
-            Vector params{
-                b.Param("offset", b.ty.u32()),
-                b.Param("value", CreateASTTypeFor(ctx, el_ty)),
-            };
-
-            auto name = b.Symbols().New(buffer.Name() + "_store");
-
-            if (auto* intrinsic = IntrinsicStoreFor(ctx.dst, el_ty, buffer)) {
-                b.Func(name, params, b.ty.void_(), nullptr,
-                       Vector{
-                           intrinsic,
-                           b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
-                       });
-            } else {
-                auto body = Switch<Vector<const ast::Statement*, 8>>(
-                    el_ty,  //
-                    [&](const core::type::Array* arr_ty) {
-                        // fn store_func(buffer : buf_ty, offset : u32, value : el_ty) {
-                        //   var array = value; // No dynamic indexing on constant arrays
-                        //   for (var i = 0u; i < array_count; i = i + 1) {
-                        //     arr[i] = el_store_func(buffer, offset + i * array_stride,
-                        //     value[i])
-                        //   }
-                        //   return arr;
-                        // }
-                        auto* array = b.Var(b.Symbols().New("array"), b.Expr("value"));
-                        auto store = StoreFunc(arr_ty->ElemType()->UnwrapRef(), buffer);
-                        auto* i = b.Var(b.Symbols().New("i"), b.Expr(0_u));
-                        auto* for_init = b.Decl(i);
-                        auto arr_cnt = arr_ty->ConstantCount();
-                        if (DAWN_UNLIKELY(!arr_cnt)) {
-                            // Non-constant counts should not be possible:
-                            // * Override-expression counts can only be applied to workgroup
-                            //   arrays, and this method only handles storage and uniform.
-                            // * Runtime-sized arrays are not storable.
-                            TINT_ICE() << "unexpected non-constant array count";
-                        }
-                        auto* for_cond = b.create<ast::BinaryExpression>(
-                            core::BinaryOp::kLessThan, b.Expr(i), b.Expr(u32(arr_cnt.value())));
-                        auto* for_cont = b.Assign(i, b.Add(i, 1_u));
-                        auto* arr_el = b.IndexAccessor(array, i);
-                        auto* el_offset = b.Add(b.Expr("offset"), b.Mul(i, u32(arr_ty->Stride())));
-                        auto* store_stmt = b.CallStmt(b.Call(store, el_offset, arr_el));
-                        auto* for_loop = b.For(for_init, for_cond, for_cont, b.Block(store_stmt));
-
-                        return Vector{b.Decl(array), for_loop};
-                    },
-                    [&](const core::type::Matrix* mat_ty) {
-                        auto* vec_ty = mat_ty->ColumnType();
-                        Symbol store = StoreFunc(vec_ty, buffer);
-                        Vector<const ast::Statement*, 4> stmts;
-                        for (uint32_t i = 0; i < mat_ty->Columns(); i++) {
-                            auto* offset = b.Add("offset", u32(i * mat_ty->ColumnStride()));
-                            auto* element = b.IndexAccessor("value", u32(i));
-                            auto* call = b.Call(store, offset, element);
-                            stmts.Push(b.CallStmt(call));
-                        }
-                        return stmts;
-                    },
-                    [&](const core::type::Struct* str) {
-                        Vector<const ast::Statement*, 8> stmts;
-                        for (auto* member : str->Members()) {
-                            auto* offset = b.Add("offset", u32(member->Offset()));
-                            auto* element = b.MemberAccessor("value", ctx.Clone(member->Name()));
-                            Symbol store = StoreFunc(member->Type()->UnwrapRef(), buffer);
-                            auto* call = b.Call(store, offset, element);
-                            stmts.Push(b.CallStmt(call));
-                        }
-                        return stmts;
-                    });
-
-                b.Func(name, params, b.ty.void_(), body);
-            }
-
-            return name;
-        });
-    }
-
-    /// AtomicFunc() returns a symbol to an builtin function that performs an  atomic operation on
-    /// the storage buffer @p buffer. The function has the signature:
-    // `fn atomic_op(offset : u32, ...) -> T`
-    /// @param el_ty the storage buffer element type
-    /// @param builtin the atomic builtin
-    /// @param buffer the symbol of the storage buffer variable, owned by the target ProgramBuilder.
-    /// @return the name of the function that performs the load
-    Symbol AtomicFunc(const core::type::Type* el_ty,
-                      const sem::BuiltinFn* builtin,
-                      const Symbol& buffer) {
-        auto fn = builtin->Fn();
-        return tint::GetOrAdd(atomic_funcs, AtomicKey{el_ty, fn, buffer}, [&] {
-            // The first parameter to all WGSL atomics is the expression to the
-            // atomic. This is replaced with two parameters: the buffer and offset.
-            Vector params{b.Param("offset", b.ty.u32())};
-
-            // Other parameters are copied as-is:
-            for (size_t i = 1; i < builtin->Parameters().Length(); i++) {
-                auto* param = builtin->Parameters()[i];
-                auto ty = CreateASTTypeFor(ctx, param->Type());
-                params.Push(b.Param("param_" + std::to_string(i), ty));
-            }
-
-            auto* atomic = IntrinsicAtomicFor(ctx.dst, fn, el_ty, buffer);
-            if (DAWN_UNLIKELY(!atomic)) {
-                TINT_ICE() << "IntrinsicAtomicFor() returned nullptr for fn " << fn << " and type "
-                           << el_ty->TypeInfo().name;
-            }
-
-            ast::Type ret_ty = CreateASTTypeFor(ctx, builtin->ReturnType());
-
-            auto name = b.Symbols().New(buffer.Name() + builtin->str());
-            b.Func(name, std::move(params), ret_ty, nullptr,
-                   Vector{
-                       atomic,
-                       b.Disable(ast::DisabledValidation::kFunctionHasNoBody),
-                   });
-            return name;
-        });
-    }
-};
-
-DecomposeMemoryAccess::Intrinsic::Intrinsic(GenerationID pid,
-                                            ast::NodeID nid,
-                                            Op o,
-                                            DataType ty,
-                                            core::AddressSpace as,
-                                            const ast::IdentifierExpression* buf)
-    : Base(pid, nid, Vector{buf}), op(o), type(ty), address_space(as) {}
-DecomposeMemoryAccess::Intrinsic::~Intrinsic() = default;
-std::string DecomposeMemoryAccess::Intrinsic::InternalName() const {
-    StringStream ss;
-    switch (op) {
-        case Op::kLoad:
-            ss << "intrinsic_load_";
-            break;
-        case Op::kStore:
-            ss << "intrinsic_store_";
-            break;
-        case Op::kAtomicLoad:
-            ss << "intrinsic_atomic_load_";
-            break;
-        case Op::kAtomicStore:
-            ss << "intrinsic_atomic_store_";
-            break;
-        case Op::kAtomicAdd:
-            ss << "intrinsic_atomic_add_";
-            break;
-        case Op::kAtomicSub:
-            ss << "intrinsic_atomic_sub_";
-            break;
-        case Op::kAtomicMax:
-            ss << "intrinsic_atomic_max_";
-            break;
-        case Op::kAtomicMin:
-            ss << "intrinsic_atomic_min_";
-            break;
-        case Op::kAtomicAnd:
-            ss << "intrinsic_atomic_and_";
-            break;
-        case Op::kAtomicOr:
-            ss << "intrinsic_atomic_or_";
-            break;
-        case Op::kAtomicXor:
-            ss << "intrinsic_atomic_xor_";
-            break;
-        case Op::kAtomicExchange:
-            ss << "intrinsic_atomic_exchange_";
-            break;
-        case Op::kAtomicCompareExchangeWeak:
-            ss << "intrinsic_atomic_compare_exchange_weak_";
-            break;
-    }
-    ss << address_space << "_";
-    switch (type) {
-        case DataType::kU32:
-            ss << "u32";
-            break;
-        case DataType::kF32:
-            ss << "f32";
-            break;
-        case DataType::kI32:
-            ss << "i32";
-            break;
-        case DataType::kF16:
-            ss << "f16";
-            break;
-        case DataType::kVec2U32:
-            ss << "vec2_u32";
-            break;
-        case DataType::kVec2F32:
-            ss << "vec2_f32";
-            break;
-        case DataType::kVec2I32:
-            ss << "vec2_i32";
-            break;
-        case DataType::kVec2F16:
-            ss << "vec2_f16";
-            break;
-        case DataType::kVec3U32:
-            ss << "vec3_u32";
-            break;
-        case DataType::kVec3F32:
-            ss << "vec3_f32";
-            break;
-        case DataType::kVec3I32:
-            ss << "vec3_i32";
-            break;
-        case DataType::kVec3F16:
-            ss << "vec3_f16";
-            break;
-        case DataType::kVec4U32:
-            ss << "vec4_u32";
-            break;
-        case DataType::kVec4F32:
-            ss << "vec4_f32";
-            break;
-        case DataType::kVec4I32:
-            ss << "vec4_i32";
-            break;
-        case DataType::kVec4F16:
-            ss << "vec4_f16";
-            break;
-    }
-    return ss.str();
-}
-
-const DecomposeMemoryAccess::Intrinsic* DecomposeMemoryAccess::Intrinsic::Clone(
-    ast::CloneContext& ctx) const {
-    auto buf = ctx.Clone(Buffer());
-    return ctx.dst->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
-        ctx.dst->ID(), ctx.dst->AllocateNodeID(), op, type, address_space, buf);
-}
-
-bool DecomposeMemoryAccess::Intrinsic::IsAtomic() const {
-    return op != Op::kLoad && op != Op::kStore;
-}
-
-const ast::IdentifierExpression* DecomposeMemoryAccess::Intrinsic::Buffer() const {
-    return dependencies[0];
-}
-
-DecomposeMemoryAccess::DecomposeMemoryAccess() = default;
-DecomposeMemoryAccess::~DecomposeMemoryAccess() = default;
-
-ast::transform::Transform::ApplyResult DecomposeMemoryAccess::Apply(
-    const Program& src,
-    const ast::transform::DataMap&,
-    ast::transform::DataMap&) const {
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    auto& sem = src.Sem();
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-    State state(ctx);
-
-    // Scan the AST nodes for storage and uniform buffer accesses. Complex
-    // expression chains (e.g. `storage_buffer.foo.bar[20].x`) are handled by
-    // maintaining an offset chain via the `state.TakeAccess()`,
-    // `state.AddAccess()` methods.
-    //
-    // Inner-most expression nodes are guaranteed to be visited first because AST
-    // nodes are fully immutable and require their children to be constructed
-    // first so their pointer can be passed to the parent's initializer.
-    for (auto* node : src.ASTNodes().Objects()) {
-        if (auto* ident = node->As<ast::IdentifierExpression>()) {
-            // X
-            if (auto* sem_ident = sem.GetVal(ident)) {
-                if (auto* user = sem_ident->UnwrapLoad()->As<sem::VariableUser>()) {
-                    if (auto* global = user->Variable()->As<sem::GlobalVariable>()) {
-                        if (global->AddressSpace() == core::AddressSpace::kStorage ||
-                            global->AddressSpace() == core::AddressSpace::kUniform) {
-                            // Variable to a storage or uniform buffer
-                            state.AddAccess(ident, {
-                                                       global,
-                                                       state.ToOffset(0u),
-                                                       global->Type()->UnwrapRef(),
-                                                   });
-                        }
-                    }
-                }
-            }
-            continue;
-        }
-
-        if (auto* accessor = node->As<ast::MemberAccessorExpression>()) {
-            // X.Y
-            auto* accessor_sem = sem.Get(accessor)->UnwrapLoad();
-            if (auto* swizzle = accessor_sem->As<sem::Swizzle>()) {
-                if (swizzle->Indices().Length() == 1) {
-                    if (auto access = state.TakeAccess(accessor->object)) {
-                        auto* vec_ty = access.type->As<core::type::Vector>();
-                        auto* offset = state.Mul(vec_ty->Type()->Size(), swizzle->Indices()[0u]);
-                        state.AddAccess(accessor, {
-                                                      access.var,
-                                                      state.Add(access.offset, offset),
-                                                      vec_ty->Type(),
-                                                  });
-                    }
-                }
-            } else {
-                if (auto access = state.TakeAccess(accessor->object)) {
-                    auto* str_ty = access.type->As<core::type::Struct>();
-                    auto* member = str_ty->FindMember(accessor->member->symbol);
-                    auto offset = member->Offset();
-                    state.AddAccess(accessor, {
-                                                  access.var,
-                                                  state.Add(access.offset, offset),
-                                                  member->Type(),
-                                              });
-                }
-            }
-            continue;
-        }
-
-        if (auto* accessor = node->As<ast::IndexAccessorExpression>()) {
-            if (auto access = state.TakeAccess(accessor->object)) {
-                // X[Y]
-                if (auto* arr = access.type->As<core::type::Array>()) {
-                    auto* offset = state.Mul(arr->Stride(), accessor->index);
-                    state.AddAccess(accessor, {
-                                                  access.var,
-                                                  state.Add(access.offset, offset),
-                                                  arr->ElemType(),
-                                              });
-                    continue;
-                }
-                if (auto* vec_ty = access.type->As<core::type::Vector>()) {
-                    auto* offset = state.Mul(vec_ty->Type()->Size(), accessor->index);
-                    state.AddAccess(accessor, {
-                                                  access.var,
-                                                  state.Add(access.offset, offset),
-                                                  vec_ty->Type(),
-                                              });
-                    continue;
-                }
-                if (auto* mat_ty = access.type->As<core::type::Matrix>()) {
-                    auto* offset = state.Mul(mat_ty->ColumnStride(), accessor->index);
-                    state.AddAccess(accessor, {
-                                                  access.var,
-                                                  state.Add(access.offset, offset),
-                                                  mat_ty->ColumnType(),
-                                              });
-                    continue;
-                }
-            }
-        }
-
-        if (auto* op = node->As<ast::UnaryOpExpression>()) {
-            if (op->op == core::UnaryOp::kAddressOf) {
-                // &X
-                if (auto access = state.TakeAccess(op->expr)) {
-                    // HLSL does not support pointers, so just take the access from the
-                    // reference and place it on the pointer.
-                    state.AddAccess(op, access);
-                    continue;
-                }
-            }
-        }
-
-        if (auto* assign = node->As<ast::AssignmentStatement>()) {
-            // X = Y
-            // Move the LHS access to a store.
-            if (auto lhs = state.TakeAccess(assign->lhs)) {
-                state.stores.emplace_back(Store{assign, lhs});
-            }
-        }
-
-        if (auto* call_expr = node->As<ast::CallExpression>()) {
-            auto* call = sem.Get(call_expr)->UnwrapMaterialize()->As<sem::Call>();
-            if (auto* builtin = call->Target()->As<sem::BuiltinFn>()) {
-                if (builtin->Fn() == wgsl::BuiltinFn::kArrayLength) {
-                    // arrayLength(X)
-                    // Don't convert X into a load, this builtin actually requires the real pointer.
-                    state.TakeAccess(call_expr->args[0]);
-                    continue;
-                }
-                if (builtin->IsAtomic()) {
-                    if (auto access = state.TakeAccess(call_expr->args[0])) {
-                        // atomic___(X)
-                        ctx.Replace(call_expr, [=, &ctx, &state] {
-                            auto* offset = access.offset->Build(ctx);
-                            auto* el_ty =
-                                access.type->UnwrapRef()->As<core::type::Atomic>()->Type();
-                            auto buffer = ctx.Clone(access.var->Declaration()->name->symbol);
-                            Symbol func = state.AtomicFunc(el_ty, builtin, buffer);
-
-                            Vector<const ast::Expression*, 8> args{offset};
-                            for (size_t i = 1; i < call_expr->args.Length(); i++) {
-                                auto* arg = call_expr->args[i];
-                                args.Push(ctx.Clone(arg));
-                            }
-                            return ctx.dst->Call(func, args);
-                        });
-                    }
-                }
-            }
-        }
-    }
-
-    // All remaining accesses are loads, transform these into calls to the
-    // corresponding load function
-    // TODO(crbug.com/tint/1784): Use `sem::Load`s instead of maintaining `state.expression_order`.
-    for (auto* expr : state.expression_order) {
-        auto access_it = state.accesses.find(expr);
-        if (access_it == state.accesses.end()) {
-            continue;
-        }
-        BufferAccess access = access_it->second;
-        ctx.Replace(expr, [=, &ctx, &state] {
-            auto* offset = access.offset->Build(ctx);
-            auto* el_ty = access.type->UnwrapRef();
-            auto buffer = ctx.Clone(access.var->Declaration()->name->symbol);
-            Symbol func = state.LoadFunc(el_ty, access.var->AddressSpace(), buffer);
-            return ctx.dst->Call(func, offset);
-        });
-    }
-
-    // And replace all storage and uniform buffer assignments with stores
-    for (auto store : state.stores) {
-        ctx.Replace(store.assignment, [=, &ctx, &state] {
-            auto* offset = store.target.offset->Build(ctx);
-            auto* el_ty = store.target.type->UnwrapRef();
-            auto* value = store.assignment->rhs;
-            auto buffer = ctx.Clone(store.target.var->Declaration()->name->symbol);
-            Symbol func = state.StoreFunc(el_ty, buffer);
-            auto* call = ctx.dst->Call(func, offset, ctx.Clone(value));
-            return ctx.dst->CallStmt(call);
-        });
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-}  // namespace tint::hlsl::writer
-
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::Offset);
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::OffsetLiteral);
diff --git a/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.h b/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.h
deleted file mode 100644
index 37f04d3..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.h
+++ /dev/null
@@ -1,143 +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_HLSL_WRITER_AST_RAISE_DECOMPOSE_MEMORY_ACCESS_H_
-#define SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_DECOMPOSE_MEMORY_ACCESS_H_
-
-#include <string>
-
-#include "src/tint/lang/wgsl/ast/internal_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::hlsl::writer {
-
-/// DecomposeMemoryAccess is a transform used to replace storage and uniform buffer accesses with a
-/// combination of load, store or atomic functions on primitive types.
-class DecomposeMemoryAccess final
-    : public Castable<DecomposeMemoryAccess, ast::transform::Transform> {
-  public:
-    /// Intrinsic is an InternalAttribute that's used to decorate a stub function so that the HLSL
-    /// transforms this into calls to
-    /// `[RW]ByteAddressBuffer.Load[N]()` or `[RW]ByteAddressBuffer.Store[N]()`,
-    /// with a possible cast.
-    class Intrinsic final : public Castable<Intrinsic, ast::InternalAttribute> {
-      public:
-        /// Intrinsic op
-        enum class Op {
-            kLoad,
-            kStore,
-            kAtomicLoad,
-            kAtomicStore,
-            kAtomicAdd,
-            kAtomicSub,
-            kAtomicMax,
-            kAtomicMin,
-            kAtomicAnd,
-            kAtomicOr,
-            kAtomicXor,
-            kAtomicExchange,
-            kAtomicCompareExchangeWeak,
-        };
-
-        /// Intrinsic data type
-        enum class DataType {
-            kU32,
-            kF32,
-            kI32,
-            kF16,
-            kVec2U32,
-            kVec2F32,
-            kVec2I32,
-            kVec2F16,
-            kVec3U32,
-            kVec3F32,
-            kVec3I32,
-            kVec3F16,
-            kVec4U32,
-            kVec4F32,
-            kVec4I32,
-            kVec4F16,
-        };
-
-        /// Constructor
-        /// @param pid the identifier of the program that owns this node
-        /// @param nid the unique node identifier
-        /// @param o the op of the intrinsic
-        /// @param type the data type of the intrinsic
-        /// @param address_space the address space of the buffer
-        /// @param buffer the storage or uniform buffer identifier
-        Intrinsic(GenerationID pid,
-                  ast::NodeID nid,
-                  Op o,
-                  DataType type,
-                  core::AddressSpace address_space,
-                  const ast::IdentifierExpression* buffer);
-        /// Destructor
-        ~Intrinsic() override;
-
-        /// @return a short description of the internal attribute which will be
-        /// displayed as `@internal(<name>)`
-        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 Intrinsic* Clone(ast::CloneContext& ctx) const override;
-
-        /// @return true if op is atomic
-        bool IsAtomic() const;
-
-        /// @return the buffer that this intrinsic operates on
-        const ast::IdentifierExpression* Buffer() const;
-
-        /// The op of the intrinsic
-        const Op op;
-
-        /// The type of the intrinsic
-        const DataType type;
-
-        /// The address space of the buffer this intrinsic operates on
-        const core::AddressSpace address_space;
-    };
-
-    /// Constructor
-    DecomposeMemoryAccess();
-    /// Destructor
-    ~DecomposeMemoryAccess() 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::hlsl::writer
-
-#endif  // SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_DECOMPOSE_MEMORY_ACCESS_H_
diff --git a/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access_test.cc b/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access_test.cc
deleted file mode 100644
index d9addc3..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access_test.cc
+++ /dev/null
@@ -1,4033 +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/hlsl/writer/ast_raise/decompose_memory_access.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using DecomposeMemoryAccessTest = ast::transform::TransformTest;
-
-TEST_F(DecomposeMemoryAccessTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<DecomposeMemoryAccess>(src));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ShouldRunStorageBuffer) {
-    auto* src = R"(
-struct Buffer {
-  i : i32,
-};
-@group(0) @binding(0) var<storage, read_write> sb : Buffer;
-)";
-
-    EXPECT_TRUE(ShouldRun<DecomposeMemoryAccess>(src));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ShouldRunUniformBuffer) {
-    auto* src = R"(
-struct Buffer {
-  i : i32,
-};
-@group(0) @binding(0) var<uniform> ub : Buffer;
-)";
-
-    EXPECT_TRUE(ShouldRun<DecomposeMemoryAccess>(src));
-}
-
-TEST_F(DecomposeMemoryAccessTest, SB_BasicLoad) {
-    auto* src = R"(
-enable f16;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_vec3_f16 : array<vec3<f16>, 2>,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var scalar_f32 : f32 = sb.scalar_f32;
-  var scalar_i32 : i32 = sb.scalar_i32;
-  var scalar_u32 : u32 = sb.scalar_u32;
-  var scalar_f16 : f16 = sb.scalar_f16;
-  var vec2_f32 : vec2<f32> = sb.vec2_f32;
-  var vec2_i32 : vec2<i32> = sb.vec2_i32;
-  var vec2_u32 : vec2<u32> = sb.vec2_u32;
-  var vec2_f16 : vec2<f16> = sb.vec2_f16;
-  var vec3_f32 : vec3<f32> = sb.vec3_f32;
-  var vec3_i32 : vec3<i32> = sb.vec3_i32;
-  var vec3_u32 : vec3<u32> = sb.vec3_u32;
-  var vec3_f16 : vec3<f16> = sb.vec3_f16;
-  var vec4_f32 : vec4<f32> = sb.vec4_f32;
-  var vec4_i32 : vec4<i32> = sb.vec4_i32;
-  var vec4_u32 : vec4<u32> = sb.vec4_u32;
-  var vec4_f16 : vec4<f16> = sb.vec4_f16;
-  var mat2x2_f32 : mat2x2<f32> = sb.mat2x2_f32;
-  var mat2x3_f32 : mat2x3<f32> = sb.mat2x3_f32;
-  var mat2x4_f32 : mat2x4<f32> = sb.mat2x4_f32;
-  var mat3x2_f32 : mat3x2<f32> = sb.mat3x2_f32;
-  var mat3x3_f32 : mat3x3<f32> = sb.mat3x3_f32;
-  var mat3x4_f32 : mat3x4<f32> = sb.mat3x4_f32;
-  var mat4x2_f32 : mat4x2<f32> = sb.mat4x2_f32;
-  var mat4x3_f32 : mat4x3<f32> = sb.mat4x3_f32;
-  var mat4x4_f32 : mat4x4<f32> = sb.mat4x4_f32;
-  var mat2x2_f16 : mat2x2<f16> = sb.mat2x2_f16;
-  var mat2x3_f16 : mat2x3<f16> = sb.mat2x3_f16;
-  var mat2x4_f16 : mat2x4<f16> = sb.mat2x4_f16;
-  var mat3x2_f16 : mat3x2<f16> = sb.mat3x2_f16;
-  var mat3x3_f16 : mat3x3<f16> = sb.mat3x3_f16;
-  var mat3x4_f16 : mat3x4<f16> = sb.mat3x4_f16;
-  var mat4x2_f16 : mat4x2<f16> = sb.mat4x2_f16;
-  var mat4x3_f16 : mat4x3<f16> = sb.mat4x3_f16;
-  var mat4x4_f16 : mat4x4<f16> = sb.mat4x4_f16;
-  var arr2_vec3_f32 : array<vec3<f32>, 2> = sb.arr2_vec3_f32;
-  var arr2_vec3_f16 : array<vec3<f16>, 2> = sb.arr2_vec3_f16;
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_vec3_f16 : array<vec3<f16>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_1(offset : u32) -> i32
-
-@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_2(offset : u32) -> u32
-
-@internal(intrinsic_load_storage_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_3(offset : u32) -> f16
-
-@internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_4(offset : u32) -> vec2<f32>
-
-@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_5(offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_6(offset : u32) -> vec2<u32>
-
-@internal(intrinsic_load_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_7(offset : u32) -> vec2<f16>
-
-@internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_8(offset : u32) -> vec3<f32>
-
-@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_9(offset : u32) -> vec3<i32>
-
-@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_10(offset : u32) -> vec3<u32>
-
-@internal(intrinsic_load_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_11(offset : u32) -> vec3<f16>
-
-@internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_12(offset : u32) -> vec4<f32>
-
-@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_13(offset : u32) -> vec4<i32>
-
-@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_14(offset : u32) -> vec4<u32>
-
-@internal(intrinsic_load_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_15(offset : u32) -> vec4<f16>
-
-fn sb_load_16(offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(sb_load_4((offset + 0u)), sb_load_4((offset + 8u)));
-}
-
-fn sb_load_17(offset : u32) -> mat2x3<f32> {
-  return mat2x3<f32>(sb_load_8((offset + 0u)), sb_load_8((offset + 16u)));
-}
-
-fn sb_load_18(offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(sb_load_12((offset + 0u)), sb_load_12((offset + 16u)));
-}
-
-fn sb_load_19(offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(sb_load_4((offset + 0u)), sb_load_4((offset + 8u)), sb_load_4((offset + 16u)));
-}
-
-fn sb_load_20(offset : u32) -> mat3x3<f32> {
-  return mat3x3<f32>(sb_load_8((offset + 0u)), sb_load_8((offset + 16u)), sb_load_8((offset + 32u)));
-}
-
-fn sb_load_21(offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(sb_load_12((offset + 0u)), sb_load_12((offset + 16u)), sb_load_12((offset + 32u)));
-}
-
-fn sb_load_22(offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(sb_load_4((offset + 0u)), sb_load_4((offset + 8u)), sb_load_4((offset + 16u)), sb_load_4((offset + 24u)));
-}
-
-fn sb_load_23(offset : u32) -> mat4x3<f32> {
-  return mat4x3<f32>(sb_load_8((offset + 0u)), sb_load_8((offset + 16u)), sb_load_8((offset + 32u)), sb_load_8((offset + 48u)));
-}
-
-fn sb_load_24(offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(sb_load_12((offset + 0u)), sb_load_12((offset + 16u)), sb_load_12((offset + 32u)), sb_load_12((offset + 48u)));
-}
-
-fn sb_load_25(offset : u32) -> mat2x2<f16> {
-  return mat2x2<f16>(sb_load_7((offset + 0u)), sb_load_7((offset + 4u)));
-}
-
-fn sb_load_26(offset : u32) -> mat2x3<f16> {
-  return mat2x3<f16>(sb_load_11((offset + 0u)), sb_load_11((offset + 8u)));
-}
-
-fn sb_load_27(offset : u32) -> mat2x4<f16> {
-  return mat2x4<f16>(sb_load_15((offset + 0u)), sb_load_15((offset + 8u)));
-}
-
-fn sb_load_28(offset : u32) -> mat3x2<f16> {
-  return mat3x2<f16>(sb_load_7((offset + 0u)), sb_load_7((offset + 4u)), sb_load_7((offset + 8u)));
-}
-
-fn sb_load_29(offset : u32) -> mat3x3<f16> {
-  return mat3x3<f16>(sb_load_11((offset + 0u)), sb_load_11((offset + 8u)), sb_load_11((offset + 16u)));
-}
-
-fn sb_load_30(offset : u32) -> mat3x4<f16> {
-  return mat3x4<f16>(sb_load_15((offset + 0u)), sb_load_15((offset + 8u)), sb_load_15((offset + 16u)));
-}
-
-fn sb_load_31(offset : u32) -> mat4x2<f16> {
-  return mat4x2<f16>(sb_load_7((offset + 0u)), sb_load_7((offset + 4u)), sb_load_7((offset + 8u)), sb_load_7((offset + 12u)));
-}
-
-fn sb_load_32(offset : u32) -> mat4x3<f16> {
-  return mat4x3<f16>(sb_load_11((offset + 0u)), sb_load_11((offset + 8u)), sb_load_11((offset + 16u)), sb_load_11((offset + 24u)));
-}
-
-fn sb_load_33(offset : u32) -> mat4x4<f16> {
-  return mat4x4<f16>(sb_load_15((offset + 0u)), sb_load_15((offset + 8u)), sb_load_15((offset + 16u)), sb_load_15((offset + 24u)));
-}
-
-fn sb_load_34(offset : u32) -> array<vec3<f32>, 2u> {
-  var arr : array<vec3<f32>, 2u>;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    arr[i] = sb_load_8((offset + (i * 16u)));
-  }
-  return arr;
-}
-
-fn sb_load_35(offset : u32) -> array<vec3<f16>, 2u> {
-  var arr_1 : array<vec3<f16>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr_1[i_1] = sb_load_11((offset + (i_1 * 8u)));
-  }
-  return arr_1;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var scalar_f32 : f32 = sb_load(0u);
-  var scalar_i32 : i32 = sb_load_1(4u);
-  var scalar_u32 : u32 = sb_load_2(8u);
-  var scalar_f16 : f16 = sb_load_3(12u);
-  var vec2_f32 : vec2<f32> = sb_load_4(16u);
-  var vec2_i32 : vec2<i32> = sb_load_5(24u);
-  var vec2_u32 : vec2<u32> = sb_load_6(32u);
-  var vec2_f16 : vec2<f16> = sb_load_7(40u);
-  var vec3_f32 : vec3<f32> = sb_load_8(48u);
-  var vec3_i32 : vec3<i32> = sb_load_9(64u);
-  var vec3_u32 : vec3<u32> = sb_load_10(80u);
-  var vec3_f16 : vec3<f16> = sb_load_11(96u);
-  var vec4_f32 : vec4<f32> = sb_load_12(112u);
-  var vec4_i32 : vec4<i32> = sb_load_13(128u);
-  var vec4_u32 : vec4<u32> = sb_load_14(144u);
-  var vec4_f16 : vec4<f16> = sb_load_15(160u);
-  var mat2x2_f32 : mat2x2<f32> = sb_load_16(168u);
-  var mat2x3_f32 : mat2x3<f32> = sb_load_17(192u);
-  var mat2x4_f32 : mat2x4<f32> = sb_load_18(224u);
-  var mat3x2_f32 : mat3x2<f32> = sb_load_19(256u);
-  var mat3x3_f32 : mat3x3<f32> = sb_load_20(288u);
-  var mat3x4_f32 : mat3x4<f32> = sb_load_21(336u);
-  var mat4x2_f32 : mat4x2<f32> = sb_load_22(384u);
-  var mat4x3_f32 : mat4x3<f32> = sb_load_23(416u);
-  var mat4x4_f32 : mat4x4<f32> = sb_load_24(480u);
-  var mat2x2_f16 : mat2x2<f16> = sb_load_25(544u);
-  var mat2x3_f16 : mat2x3<f16> = sb_load_26(552u);
-  var mat2x4_f16 : mat2x4<f16> = sb_load_27(568u);
-  var mat3x2_f16 : mat3x2<f16> = sb_load_28(584u);
-  var mat3x3_f16 : mat3x3<f16> = sb_load_29(600u);
-  var mat3x4_f16 : mat3x4<f16> = sb_load_30(624u);
-  var mat4x2_f16 : mat4x2<f16> = sb_load_31(648u);
-  var mat4x3_f16 : mat4x3<f16> = sb_load_32(664u);
-  var mat4x4_f16 : mat4x4<f16> = sb_load_33(696u);
-  var arr2_vec3_f32 : array<vec3<f32>, 2> = sb_load_34(736u);
-  var arr2_vec3_f16 : array<vec3<f16>, 2> = sb_load_35(768u);
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, SB_BasicLoad_OutOfOrder) {
-    auto* src = R"(
-enable f16;
-
-@compute @workgroup_size(1)
-fn main() {
-  var scalar_f32 : f32 = sb.scalar_f32;
-  var scalar_i32 : i32 = sb.scalar_i32;
-  var scalar_u32 : u32 = sb.scalar_u32;
-  var scalar_f16 : f16 = sb.scalar_f16;
-  var vec2_f32 : vec2<f32> = sb.vec2_f32;
-  var vec2_i32 : vec2<i32> = sb.vec2_i32;
-  var vec2_u32 : vec2<u32> = sb.vec2_u32;
-  var vec2_f16 : vec2<f16> = sb.vec2_f16;
-  var vec3_f32 : vec3<f32> = sb.vec3_f32;
-  var vec3_i32 : vec3<i32> = sb.vec3_i32;
-  var vec3_u32 : vec3<u32> = sb.vec3_u32;
-  var vec3_f16 : vec3<f16> = sb.vec3_f16;
-  var vec4_f32 : vec4<f32> = sb.vec4_f32;
-  var vec4_i32 : vec4<i32> = sb.vec4_i32;
-  var vec4_u32 : vec4<u32> = sb.vec4_u32;
-  var vec4_f16 : vec4<f16> = sb.vec4_f16;
-  var mat2x2_f32 : mat2x2<f32> = sb.mat2x2_f32;
-  var mat2x3_f32 : mat2x3<f32> = sb.mat2x3_f32;
-  var mat2x4_f32 : mat2x4<f32> = sb.mat2x4_f32;
-  var mat3x2_f32 : mat3x2<f32> = sb.mat3x2_f32;
-  var mat3x3_f32 : mat3x3<f32> = sb.mat3x3_f32;
-  var mat3x4_f32 : mat3x4<f32> = sb.mat3x4_f32;
-  var mat4x2_f32 : mat4x2<f32> = sb.mat4x2_f32;
-  var mat4x3_f32 : mat4x3<f32> = sb.mat4x3_f32;
-  var mat4x4_f32 : mat4x4<f32> = sb.mat4x4_f32;
-  var mat2x2_f16 : mat2x2<f16> = sb.mat2x2_f16;
-  var mat2x3_f16 : mat2x3<f16> = sb.mat2x3_f16;
-  var mat2x4_f16 : mat2x4<f16> = sb.mat2x4_f16;
-  var mat3x2_f16 : mat3x2<f16> = sb.mat3x2_f16;
-  var mat3x3_f16 : mat3x3<f16> = sb.mat3x3_f16;
-  var mat3x4_f16 : mat3x4<f16> = sb.mat3x4_f16;
-  var mat4x2_f16 : mat4x2<f16> = sb.mat4x2_f16;
-  var mat4x3_f16 : mat4x3<f16> = sb.mat4x3_f16;
-  var mat4x4_f16 : mat4x4<f16> = sb.mat4x4_f16;
-  var arr2_vec3_f32 : array<vec3<f32>, 2> = sb.arr2_vec3_f32;
-  var arr2_vec3_f16 : array<vec3<f16>, 2> = sb.arr2_vec3_f16;
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_vec3_f16 : array<vec3<f16>, 2>,
-};
-)";
-
-    auto* expect = R"(
-enable f16;
-
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_1(offset : u32) -> i32
-
-@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_2(offset : u32) -> u32
-
-@internal(intrinsic_load_storage_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_3(offset : u32) -> f16
-
-@internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_4(offset : u32) -> vec2<f32>
-
-@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_5(offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_6(offset : u32) -> vec2<u32>
-
-@internal(intrinsic_load_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_7(offset : u32) -> vec2<f16>
-
-@internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_8(offset : u32) -> vec3<f32>
-
-@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_9(offset : u32) -> vec3<i32>
-
-@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_10(offset : u32) -> vec3<u32>
-
-@internal(intrinsic_load_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_11(offset : u32) -> vec3<f16>
-
-@internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_12(offset : u32) -> vec4<f32>
-
-@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_13(offset : u32) -> vec4<i32>
-
-@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_14(offset : u32) -> vec4<u32>
-
-@internal(intrinsic_load_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_15(offset : u32) -> vec4<f16>
-
-fn sb_load_16(offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(sb_load_4((offset + 0u)), sb_load_4((offset + 8u)));
-}
-
-fn sb_load_17(offset : u32) -> mat2x3<f32> {
-  return mat2x3<f32>(sb_load_8((offset + 0u)), sb_load_8((offset + 16u)));
-}
-
-fn sb_load_18(offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(sb_load_12((offset + 0u)), sb_load_12((offset + 16u)));
-}
-
-fn sb_load_19(offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(sb_load_4((offset + 0u)), sb_load_4((offset + 8u)), sb_load_4((offset + 16u)));
-}
-
-fn sb_load_20(offset : u32) -> mat3x3<f32> {
-  return mat3x3<f32>(sb_load_8((offset + 0u)), sb_load_8((offset + 16u)), sb_load_8((offset + 32u)));
-}
-
-fn sb_load_21(offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(sb_load_12((offset + 0u)), sb_load_12((offset + 16u)), sb_load_12((offset + 32u)));
-}
-
-fn sb_load_22(offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(sb_load_4((offset + 0u)), sb_load_4((offset + 8u)), sb_load_4((offset + 16u)), sb_load_4((offset + 24u)));
-}
-
-fn sb_load_23(offset : u32) -> mat4x3<f32> {
-  return mat4x3<f32>(sb_load_8((offset + 0u)), sb_load_8((offset + 16u)), sb_load_8((offset + 32u)), sb_load_8((offset + 48u)));
-}
-
-fn sb_load_24(offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(sb_load_12((offset + 0u)), sb_load_12((offset + 16u)), sb_load_12((offset + 32u)), sb_load_12((offset + 48u)));
-}
-
-fn sb_load_25(offset : u32) -> mat2x2<f16> {
-  return mat2x2<f16>(sb_load_7((offset + 0u)), sb_load_7((offset + 4u)));
-}
-
-fn sb_load_26(offset : u32) -> mat2x3<f16> {
-  return mat2x3<f16>(sb_load_11((offset + 0u)), sb_load_11((offset + 8u)));
-}
-
-fn sb_load_27(offset : u32) -> mat2x4<f16> {
-  return mat2x4<f16>(sb_load_15((offset + 0u)), sb_load_15((offset + 8u)));
-}
-
-fn sb_load_28(offset : u32) -> mat3x2<f16> {
-  return mat3x2<f16>(sb_load_7((offset + 0u)), sb_load_7((offset + 4u)), sb_load_7((offset + 8u)));
-}
-
-fn sb_load_29(offset : u32) -> mat3x3<f16> {
-  return mat3x3<f16>(sb_load_11((offset + 0u)), sb_load_11((offset + 8u)), sb_load_11((offset + 16u)));
-}
-
-fn sb_load_30(offset : u32) -> mat3x4<f16> {
-  return mat3x4<f16>(sb_load_15((offset + 0u)), sb_load_15((offset + 8u)), sb_load_15((offset + 16u)));
-}
-
-fn sb_load_31(offset : u32) -> mat4x2<f16> {
-  return mat4x2<f16>(sb_load_7((offset + 0u)), sb_load_7((offset + 4u)), sb_load_7((offset + 8u)), sb_load_7((offset + 12u)));
-}
-
-fn sb_load_32(offset : u32) -> mat4x3<f16> {
-  return mat4x3<f16>(sb_load_11((offset + 0u)), sb_load_11((offset + 8u)), sb_load_11((offset + 16u)), sb_load_11((offset + 24u)));
-}
-
-fn sb_load_33(offset : u32) -> mat4x4<f16> {
-  return mat4x4<f16>(sb_load_15((offset + 0u)), sb_load_15((offset + 8u)), sb_load_15((offset + 16u)), sb_load_15((offset + 24u)));
-}
-
-fn sb_load_34(offset : u32) -> array<vec3<f32>, 2u> {
-  var arr : array<vec3<f32>, 2u>;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    arr[i] = sb_load_8((offset + (i * 16u)));
-  }
-  return arr;
-}
-
-fn sb_load_35(offset : u32) -> array<vec3<f16>, 2u> {
-  var arr_1 : array<vec3<f16>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr_1[i_1] = sb_load_11((offset + (i_1 * 8u)));
-  }
-  return arr_1;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var scalar_f32 : f32 = sb_load(0u);
-  var scalar_i32 : i32 = sb_load_1(4u);
-  var scalar_u32 : u32 = sb_load_2(8u);
-  var scalar_f16 : f16 = sb_load_3(12u);
-  var vec2_f32 : vec2<f32> = sb_load_4(16u);
-  var vec2_i32 : vec2<i32> = sb_load_5(24u);
-  var vec2_u32 : vec2<u32> = sb_load_6(32u);
-  var vec2_f16 : vec2<f16> = sb_load_7(40u);
-  var vec3_f32 : vec3<f32> = sb_load_8(48u);
-  var vec3_i32 : vec3<i32> = sb_load_9(64u);
-  var vec3_u32 : vec3<u32> = sb_load_10(80u);
-  var vec3_f16 : vec3<f16> = sb_load_11(96u);
-  var vec4_f32 : vec4<f32> = sb_load_12(112u);
-  var vec4_i32 : vec4<i32> = sb_load_13(128u);
-  var vec4_u32 : vec4<u32> = sb_load_14(144u);
-  var vec4_f16 : vec4<f16> = sb_load_15(160u);
-  var mat2x2_f32 : mat2x2<f32> = sb_load_16(168u);
-  var mat2x3_f32 : mat2x3<f32> = sb_load_17(192u);
-  var mat2x4_f32 : mat2x4<f32> = sb_load_18(224u);
-  var mat3x2_f32 : mat3x2<f32> = sb_load_19(256u);
-  var mat3x3_f32 : mat3x3<f32> = sb_load_20(288u);
-  var mat3x4_f32 : mat3x4<f32> = sb_load_21(336u);
-  var mat4x2_f32 : mat4x2<f32> = sb_load_22(384u);
-  var mat4x3_f32 : mat4x3<f32> = sb_load_23(416u);
-  var mat4x4_f32 : mat4x4<f32> = sb_load_24(480u);
-  var mat2x2_f16 : mat2x2<f16> = sb_load_25(544u);
-  var mat2x3_f16 : mat2x3<f16> = sb_load_26(552u);
-  var mat2x4_f16 : mat2x4<f16> = sb_load_27(568u);
-  var mat3x2_f16 : mat3x2<f16> = sb_load_28(584u);
-  var mat3x3_f16 : mat3x3<f16> = sb_load_29(600u);
-  var mat3x4_f16 : mat3x4<f16> = sb_load_30(624u);
-  var mat4x2_f16 : mat4x2<f16> = sb_load_31(648u);
-  var mat4x3_f16 : mat4x3<f16> = sb_load_32(664u);
-  var mat4x4_f16 : mat4x4<f16> = sb_load_33(696u);
-  var arr2_vec3_f32 : array<vec3<f32>, 2> = sb_load_34(736u);
-  var arr2_vec3_f16 : array<vec3<f16>, 2> = sb_load_35(768u);
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_vec3_f16 : array<vec3<f16>, 2>,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, UB_BasicLoad) {
-    auto* src = R"(
-enable f16;
-
-struct UB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-};
-
-@group(0) @binding(0) var<uniform> ub : UB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var scalar_f32 : f32 = ub.scalar_f32;
-  var scalar_i32 : i32 = ub.scalar_i32;
-  var scalar_u32 : u32 = ub.scalar_u32;
-  var scalar_f16 : f16 = ub.scalar_f16;
-  var vec2_f32 : vec2<f32> = ub.vec2_f32;
-  var vec2_i32 : vec2<i32> = ub.vec2_i32;
-  var vec2_u32 : vec2<u32> = ub.vec2_u32;
-  var vec2_f16 : vec2<f16> = ub.vec2_f16;
-  var vec3_f32 : vec3<f32> = ub.vec3_f32;
-  var vec3_i32 : vec3<i32> = ub.vec3_i32;
-  var vec3_u32 : vec3<u32> = ub.vec3_u32;
-  var vec3_f16 : vec3<f16> = ub.vec3_f16;
-  var vec4_f32 : vec4<f32> = ub.vec4_f32;
-  var vec4_i32 : vec4<i32> = ub.vec4_i32;
-  var vec4_u32 : vec4<u32> = ub.vec4_u32;
-  var vec4_f16 : vec4<f16> = ub.vec4_f16;
-  var mat2x2_f32 : mat2x2<f32> = ub.mat2x2_f32;
-  var mat2x3_f32 : mat2x3<f32> = ub.mat2x3_f32;
-  var mat2x4_f32 : mat2x4<f32> = ub.mat2x4_f32;
-  var mat3x2_f32 : mat3x2<f32> = ub.mat3x2_f32;
-  var mat3x3_f32 : mat3x3<f32> = ub.mat3x3_f32;
-  var mat3x4_f32 : mat3x4<f32> = ub.mat3x4_f32;
-  var mat4x2_f32 : mat4x2<f32> = ub.mat4x2_f32;
-  var mat4x3_f32 : mat4x3<f32> = ub.mat4x3_f32;
-  var mat4x4_f32 : mat4x4<f32> = ub.mat4x4_f32;
-  var mat2x2_f16 : mat2x2<f16> = ub.mat2x2_f16;
-  var mat2x3_f16 : mat2x3<f16> = ub.mat2x3_f16;
-  var mat2x4_f16 : mat2x4<f16> = ub.mat2x4_f16;
-  var mat3x2_f16 : mat3x2<f16> = ub.mat3x2_f16;
-  var mat3x3_f16 : mat3x3<f16> = ub.mat3x3_f16;
-  var mat3x4_f16 : mat3x4<f16> = ub.mat3x4_f16;
-  var mat4x2_f16 : mat4x2<f16> = ub.mat4x2_f16;
-  var mat4x3_f16 : mat4x3<f16> = ub.mat4x3_f16;
-  var mat4x4_f16 : mat4x4<f16> = ub.mat4x4_f16;
-  var arr2_vec3_f32 : array<vec3<f32>, 2> = ub.arr2_vec3_f32;
-  var arr2_mat4x2_f16 : array<mat4x2<f16>, 2> = ub.arr2_mat4x2_f16;
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-struct UB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-}
-
-@group(0) @binding(0) var<uniform> ub : UB;
-
-@internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
-fn ub_load(offset : u32) -> f32
-
-@internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
-fn ub_load_1(offset : u32) -> i32
-
-@internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
-fn ub_load_2(offset : u32) -> u32
-
-@internal(intrinsic_load_uniform_f16) @internal(disable_validation__function_has_no_body)
-fn ub_load_3(offset : u32) -> f16
-
-@internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn ub_load_4(offset : u32) -> vec2<f32>
-
-@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn ub_load_5(offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn ub_load_6(offset : u32) -> vec2<u32>
-
-@internal(intrinsic_load_uniform_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn ub_load_7(offset : u32) -> vec2<f16>
-
-@internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn ub_load_8(offset : u32) -> vec3<f32>
-
-@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn ub_load_9(offset : u32) -> vec3<i32>
-
-@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn ub_load_10(offset : u32) -> vec3<u32>
-
-@internal(intrinsic_load_uniform_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn ub_load_11(offset : u32) -> vec3<f16>
-
-@internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn ub_load_12(offset : u32) -> vec4<f32>
-
-@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn ub_load_13(offset : u32) -> vec4<i32>
-
-@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn ub_load_14(offset : u32) -> vec4<u32>
-
-@internal(intrinsic_load_uniform_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn ub_load_15(offset : u32) -> vec4<f16>
-
-fn ub_load_16(offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(ub_load_4((offset + 0u)), ub_load_4((offset + 8u)));
-}
-
-fn ub_load_17(offset : u32) -> mat2x3<f32> {
-  return mat2x3<f32>(ub_load_8((offset + 0u)), ub_load_8((offset + 16u)));
-}
-
-fn ub_load_18(offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(ub_load_12((offset + 0u)), ub_load_12((offset + 16u)));
-}
-
-fn ub_load_19(offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(ub_load_4((offset + 0u)), ub_load_4((offset + 8u)), ub_load_4((offset + 16u)));
-}
-
-fn ub_load_20(offset : u32) -> mat3x3<f32> {
-  return mat3x3<f32>(ub_load_8((offset + 0u)), ub_load_8((offset + 16u)), ub_load_8((offset + 32u)));
-}
-
-fn ub_load_21(offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(ub_load_12((offset + 0u)), ub_load_12((offset + 16u)), ub_load_12((offset + 32u)));
-}
-
-fn ub_load_22(offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(ub_load_4((offset + 0u)), ub_load_4((offset + 8u)), ub_load_4((offset + 16u)), ub_load_4((offset + 24u)));
-}
-
-fn ub_load_23(offset : u32) -> mat4x3<f32> {
-  return mat4x3<f32>(ub_load_8((offset + 0u)), ub_load_8((offset + 16u)), ub_load_8((offset + 32u)), ub_load_8((offset + 48u)));
-}
-
-fn ub_load_24(offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(ub_load_12((offset + 0u)), ub_load_12((offset + 16u)), ub_load_12((offset + 32u)), ub_load_12((offset + 48u)));
-}
-
-fn ub_load_25(offset : u32) -> mat2x2<f16> {
-  return mat2x2<f16>(ub_load_7((offset + 0u)), ub_load_7((offset + 4u)));
-}
-
-fn ub_load_26(offset : u32) -> mat2x3<f16> {
-  return mat2x3<f16>(ub_load_11((offset + 0u)), ub_load_11((offset + 8u)));
-}
-
-fn ub_load_27(offset : u32) -> mat2x4<f16> {
-  return mat2x4<f16>(ub_load_15((offset + 0u)), ub_load_15((offset + 8u)));
-}
-
-fn ub_load_28(offset : u32) -> mat3x2<f16> {
-  return mat3x2<f16>(ub_load_7((offset + 0u)), ub_load_7((offset + 4u)), ub_load_7((offset + 8u)));
-}
-
-fn ub_load_29(offset : u32) -> mat3x3<f16> {
-  return mat3x3<f16>(ub_load_11((offset + 0u)), ub_load_11((offset + 8u)), ub_load_11((offset + 16u)));
-}
-
-fn ub_load_30(offset : u32) -> mat3x4<f16> {
-  return mat3x4<f16>(ub_load_15((offset + 0u)), ub_load_15((offset + 8u)), ub_load_15((offset + 16u)));
-}
-
-fn ub_load_31(offset : u32) -> mat4x2<f16> {
-  return mat4x2<f16>(ub_load_7((offset + 0u)), ub_load_7((offset + 4u)), ub_load_7((offset + 8u)), ub_load_7((offset + 12u)));
-}
-
-fn ub_load_32(offset : u32) -> mat4x3<f16> {
-  return mat4x3<f16>(ub_load_11((offset + 0u)), ub_load_11((offset + 8u)), ub_load_11((offset + 16u)), ub_load_11((offset + 24u)));
-}
-
-fn ub_load_33(offset : u32) -> mat4x4<f16> {
-  return mat4x4<f16>(ub_load_15((offset + 0u)), ub_load_15((offset + 8u)), ub_load_15((offset + 16u)), ub_load_15((offset + 24u)));
-}
-
-fn ub_load_34(offset : u32) -> array<vec3<f32>, 2u> {
-  var arr : array<vec3<f32>, 2u>;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    arr[i] = ub_load_8((offset + (i * 16u)));
-  }
-  return arr;
-}
-
-fn ub_load_35(offset : u32) -> array<mat4x2<f16>, 2u> {
-  var arr_1 : array<mat4x2<f16>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr_1[i_1] = ub_load_31((offset + (i_1 * 16u)));
-  }
-  return arr_1;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var scalar_f32 : f32 = ub_load(0u);
-  var scalar_i32 : i32 = ub_load_1(4u);
-  var scalar_u32 : u32 = ub_load_2(8u);
-  var scalar_f16 : f16 = ub_load_3(12u);
-  var vec2_f32 : vec2<f32> = ub_load_4(16u);
-  var vec2_i32 : vec2<i32> = ub_load_5(24u);
-  var vec2_u32 : vec2<u32> = ub_load_6(32u);
-  var vec2_f16 : vec2<f16> = ub_load_7(40u);
-  var vec3_f32 : vec3<f32> = ub_load_8(48u);
-  var vec3_i32 : vec3<i32> = ub_load_9(64u);
-  var vec3_u32 : vec3<u32> = ub_load_10(80u);
-  var vec3_f16 : vec3<f16> = ub_load_11(96u);
-  var vec4_f32 : vec4<f32> = ub_load_12(112u);
-  var vec4_i32 : vec4<i32> = ub_load_13(128u);
-  var vec4_u32 : vec4<u32> = ub_load_14(144u);
-  var vec4_f16 : vec4<f16> = ub_load_15(160u);
-  var mat2x2_f32 : mat2x2<f32> = ub_load_16(168u);
-  var mat2x3_f32 : mat2x3<f32> = ub_load_17(192u);
-  var mat2x4_f32 : mat2x4<f32> = ub_load_18(224u);
-  var mat3x2_f32 : mat3x2<f32> = ub_load_19(256u);
-  var mat3x3_f32 : mat3x3<f32> = ub_load_20(288u);
-  var mat3x4_f32 : mat3x4<f32> = ub_load_21(336u);
-  var mat4x2_f32 : mat4x2<f32> = ub_load_22(384u);
-  var mat4x3_f32 : mat4x3<f32> = ub_load_23(416u);
-  var mat4x4_f32 : mat4x4<f32> = ub_load_24(480u);
-  var mat2x2_f16 : mat2x2<f16> = ub_load_25(544u);
-  var mat2x3_f16 : mat2x3<f16> = ub_load_26(552u);
-  var mat2x4_f16 : mat2x4<f16> = ub_load_27(568u);
-  var mat3x2_f16 : mat3x2<f16> = ub_load_28(584u);
-  var mat3x3_f16 : mat3x3<f16> = ub_load_29(600u);
-  var mat3x4_f16 : mat3x4<f16> = ub_load_30(624u);
-  var mat4x2_f16 : mat4x2<f16> = ub_load_31(648u);
-  var mat4x3_f16 : mat4x3<f16> = ub_load_32(664u);
-  var mat4x4_f16 : mat4x4<f16> = ub_load_33(696u);
-  var arr2_vec3_f32 : array<vec3<f32>, 2> = ub_load_34(736u);
-  var arr2_mat4x2_f16 : array<mat4x2<f16>, 2> = ub_load_35(768u);
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, UB_BasicLoad_OutOfOrder) {
-    auto* src = R"(
-enable f16;
-
-@compute @workgroup_size(1)
-fn main() {
-  var scalar_f32 : f32 = ub.scalar_f32;
-  var scalar_i32 : i32 = ub.scalar_i32;
-  var scalar_u32 : u32 = ub.scalar_u32;
-  var scalar_f16 : f16 = ub.scalar_f16;
-  var vec2_f32 : vec2<f32> = ub.vec2_f32;
-  var vec2_i32 : vec2<i32> = ub.vec2_i32;
-  var vec2_u32 : vec2<u32> = ub.vec2_u32;
-  var vec2_f16 : vec2<f16> = ub.vec2_f16;
-  var vec3_f32 : vec3<f32> = ub.vec3_f32;
-  var vec3_i32 : vec3<i32> = ub.vec3_i32;
-  var vec3_u32 : vec3<u32> = ub.vec3_u32;
-  var vec3_f16 : vec3<f16> = ub.vec3_f16;
-  var vec4_f32 : vec4<f32> = ub.vec4_f32;
-  var vec4_i32 : vec4<i32> = ub.vec4_i32;
-  var vec4_u32 : vec4<u32> = ub.vec4_u32;
-  var vec4_f16 : vec4<f16> = ub.vec4_f16;
-  var mat2x2_f32 : mat2x2<f32> = ub.mat2x2_f32;
-  var mat2x3_f32 : mat2x3<f32> = ub.mat2x3_f32;
-  var mat2x4_f32 : mat2x4<f32> = ub.mat2x4_f32;
-  var mat3x2_f32 : mat3x2<f32> = ub.mat3x2_f32;
-  var mat3x3_f32 : mat3x3<f32> = ub.mat3x3_f32;
-  var mat3x4_f32 : mat3x4<f32> = ub.mat3x4_f32;
-  var mat4x2_f32 : mat4x2<f32> = ub.mat4x2_f32;
-  var mat4x3_f32 : mat4x3<f32> = ub.mat4x3_f32;
-  var mat4x4_f32 : mat4x4<f32> = ub.mat4x4_f32;
-  var mat2x2_f16 : mat2x2<f16> = ub.mat2x2_f16;
-  var mat2x3_f16 : mat2x3<f16> = ub.mat2x3_f16;
-  var mat2x4_f16 : mat2x4<f16> = ub.mat2x4_f16;
-  var mat3x2_f16 : mat3x2<f16> = ub.mat3x2_f16;
-  var mat3x3_f16 : mat3x3<f16> = ub.mat3x3_f16;
-  var mat3x4_f16 : mat3x4<f16> = ub.mat3x4_f16;
-  var mat4x2_f16 : mat4x2<f16> = ub.mat4x2_f16;
-  var mat4x3_f16 : mat4x3<f16> = ub.mat4x3_f16;
-  var mat4x4_f16 : mat4x4<f16> = ub.mat4x4_f16;
-  var arr2_vec3_f32 : array<vec3<f32>, 2> = ub.arr2_vec3_f32;
-  var arr2_mat4x2_f16 : array<mat4x2<f16>, 2> = ub.arr2_mat4x2_f16;
-}
-
-@group(0) @binding(0) var<uniform> ub : UB;
-
-struct UB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-};
-)";
-
-    auto* expect = R"(
-enable f16;
-
-@internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
-fn ub_load(offset : u32) -> f32
-
-@internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
-fn ub_load_1(offset : u32) -> i32
-
-@internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
-fn ub_load_2(offset : u32) -> u32
-
-@internal(intrinsic_load_uniform_f16) @internal(disable_validation__function_has_no_body)
-fn ub_load_3(offset : u32) -> f16
-
-@internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn ub_load_4(offset : u32) -> vec2<f32>
-
-@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn ub_load_5(offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn ub_load_6(offset : u32) -> vec2<u32>
-
-@internal(intrinsic_load_uniform_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn ub_load_7(offset : u32) -> vec2<f16>
-
-@internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn ub_load_8(offset : u32) -> vec3<f32>
-
-@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn ub_load_9(offset : u32) -> vec3<i32>
-
-@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn ub_load_10(offset : u32) -> vec3<u32>
-
-@internal(intrinsic_load_uniform_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn ub_load_11(offset : u32) -> vec3<f16>
-
-@internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn ub_load_12(offset : u32) -> vec4<f32>
-
-@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn ub_load_13(offset : u32) -> vec4<i32>
-
-@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn ub_load_14(offset : u32) -> vec4<u32>
-
-@internal(intrinsic_load_uniform_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn ub_load_15(offset : u32) -> vec4<f16>
-
-fn ub_load_16(offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(ub_load_4((offset + 0u)), ub_load_4((offset + 8u)));
-}
-
-fn ub_load_17(offset : u32) -> mat2x3<f32> {
-  return mat2x3<f32>(ub_load_8((offset + 0u)), ub_load_8((offset + 16u)));
-}
-
-fn ub_load_18(offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(ub_load_12((offset + 0u)), ub_load_12((offset + 16u)));
-}
-
-fn ub_load_19(offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(ub_load_4((offset + 0u)), ub_load_4((offset + 8u)), ub_load_4((offset + 16u)));
-}
-
-fn ub_load_20(offset : u32) -> mat3x3<f32> {
-  return mat3x3<f32>(ub_load_8((offset + 0u)), ub_load_8((offset + 16u)), ub_load_8((offset + 32u)));
-}
-
-fn ub_load_21(offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(ub_load_12((offset + 0u)), ub_load_12((offset + 16u)), ub_load_12((offset + 32u)));
-}
-
-fn ub_load_22(offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(ub_load_4((offset + 0u)), ub_load_4((offset + 8u)), ub_load_4((offset + 16u)), ub_load_4((offset + 24u)));
-}
-
-fn ub_load_23(offset : u32) -> mat4x3<f32> {
-  return mat4x3<f32>(ub_load_8((offset + 0u)), ub_load_8((offset + 16u)), ub_load_8((offset + 32u)), ub_load_8((offset + 48u)));
-}
-
-fn ub_load_24(offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(ub_load_12((offset + 0u)), ub_load_12((offset + 16u)), ub_load_12((offset + 32u)), ub_load_12((offset + 48u)));
-}
-
-fn ub_load_25(offset : u32) -> mat2x2<f16> {
-  return mat2x2<f16>(ub_load_7((offset + 0u)), ub_load_7((offset + 4u)));
-}
-
-fn ub_load_26(offset : u32) -> mat2x3<f16> {
-  return mat2x3<f16>(ub_load_11((offset + 0u)), ub_load_11((offset + 8u)));
-}
-
-fn ub_load_27(offset : u32) -> mat2x4<f16> {
-  return mat2x4<f16>(ub_load_15((offset + 0u)), ub_load_15((offset + 8u)));
-}
-
-fn ub_load_28(offset : u32) -> mat3x2<f16> {
-  return mat3x2<f16>(ub_load_7((offset + 0u)), ub_load_7((offset + 4u)), ub_load_7((offset + 8u)));
-}
-
-fn ub_load_29(offset : u32) -> mat3x3<f16> {
-  return mat3x3<f16>(ub_load_11((offset + 0u)), ub_load_11((offset + 8u)), ub_load_11((offset + 16u)));
-}
-
-fn ub_load_30(offset : u32) -> mat3x4<f16> {
-  return mat3x4<f16>(ub_load_15((offset + 0u)), ub_load_15((offset + 8u)), ub_load_15((offset + 16u)));
-}
-
-fn ub_load_31(offset : u32) -> mat4x2<f16> {
-  return mat4x2<f16>(ub_load_7((offset + 0u)), ub_load_7((offset + 4u)), ub_load_7((offset + 8u)), ub_load_7((offset + 12u)));
-}
-
-fn ub_load_32(offset : u32) -> mat4x3<f16> {
-  return mat4x3<f16>(ub_load_11((offset + 0u)), ub_load_11((offset + 8u)), ub_load_11((offset + 16u)), ub_load_11((offset + 24u)));
-}
-
-fn ub_load_33(offset : u32) -> mat4x4<f16> {
-  return mat4x4<f16>(ub_load_15((offset + 0u)), ub_load_15((offset + 8u)), ub_load_15((offset + 16u)), ub_load_15((offset + 24u)));
-}
-
-fn ub_load_34(offset : u32) -> array<vec3<f32>, 2u> {
-  var arr : array<vec3<f32>, 2u>;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    arr[i] = ub_load_8((offset + (i * 16u)));
-  }
-  return arr;
-}
-
-fn ub_load_35(offset : u32) -> array<mat4x2<f16>, 2u> {
-  var arr_1 : array<mat4x2<f16>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr_1[i_1] = ub_load_31((offset + (i_1 * 16u)));
-  }
-  return arr_1;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var scalar_f32 : f32 = ub_load(0u);
-  var scalar_i32 : i32 = ub_load_1(4u);
-  var scalar_u32 : u32 = ub_load_2(8u);
-  var scalar_f16 : f16 = ub_load_3(12u);
-  var vec2_f32 : vec2<f32> = ub_load_4(16u);
-  var vec2_i32 : vec2<i32> = ub_load_5(24u);
-  var vec2_u32 : vec2<u32> = ub_load_6(32u);
-  var vec2_f16 : vec2<f16> = ub_load_7(40u);
-  var vec3_f32 : vec3<f32> = ub_load_8(48u);
-  var vec3_i32 : vec3<i32> = ub_load_9(64u);
-  var vec3_u32 : vec3<u32> = ub_load_10(80u);
-  var vec3_f16 : vec3<f16> = ub_load_11(96u);
-  var vec4_f32 : vec4<f32> = ub_load_12(112u);
-  var vec4_i32 : vec4<i32> = ub_load_13(128u);
-  var vec4_u32 : vec4<u32> = ub_load_14(144u);
-  var vec4_f16 : vec4<f16> = ub_load_15(160u);
-  var mat2x2_f32 : mat2x2<f32> = ub_load_16(168u);
-  var mat2x3_f32 : mat2x3<f32> = ub_load_17(192u);
-  var mat2x4_f32 : mat2x4<f32> = ub_load_18(224u);
-  var mat3x2_f32 : mat3x2<f32> = ub_load_19(256u);
-  var mat3x3_f32 : mat3x3<f32> = ub_load_20(288u);
-  var mat3x4_f32 : mat3x4<f32> = ub_load_21(336u);
-  var mat4x2_f32 : mat4x2<f32> = ub_load_22(384u);
-  var mat4x3_f32 : mat4x3<f32> = ub_load_23(416u);
-  var mat4x4_f32 : mat4x4<f32> = ub_load_24(480u);
-  var mat2x2_f16 : mat2x2<f16> = ub_load_25(544u);
-  var mat2x3_f16 : mat2x3<f16> = ub_load_26(552u);
-  var mat2x4_f16 : mat2x4<f16> = ub_load_27(568u);
-  var mat3x2_f16 : mat3x2<f16> = ub_load_28(584u);
-  var mat3x3_f16 : mat3x3<f16> = ub_load_29(600u);
-  var mat3x4_f16 : mat3x4<f16> = ub_load_30(624u);
-  var mat4x2_f16 : mat4x2<f16> = ub_load_31(648u);
-  var mat4x3_f16 : mat4x3<f16> = ub_load_32(664u);
-  var mat4x4_f16 : mat4x4<f16> = ub_load_33(696u);
-  var arr2_vec3_f32 : array<vec3<f32>, 2> = ub_load_34(736u);
-  var arr2_mat4x2_f16 : array<mat4x2<f16>, 2> = ub_load_35(768u);
-}
-
-@group(0) @binding(0) var<uniform> ub : UB;
-
-struct UB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, SB_BasicStore) {
-    auto* src = R"(
-enable f16;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  sb.scalar_f32 = f32();
-  sb.scalar_i32 = i32();
-  sb.scalar_u32 = u32();
-  sb.scalar_f16 = f16();
-  sb.vec2_f32 = vec2<f32>();
-  sb.vec2_i32 = vec2<i32>();
-  sb.vec2_u32 = vec2<u32>();
-  sb.vec2_f16 = vec2<f16>();
-  sb.vec3_f32 = vec3<f32>();
-  sb.vec3_i32 = vec3<i32>();
-  sb.vec3_u32 = vec3<u32>();
-  sb.vec3_f16 = vec3<f16>();
-  sb.vec4_f32 = vec4<f32>();
-  sb.vec4_i32 = vec4<i32>();
-  sb.vec4_u32 = vec4<u32>();
-  sb.vec4_f16 = vec4<f16>();
-  sb.mat2x2_f32 = mat2x2<f32>();
-  sb.mat2x3_f32 = mat2x3<f32>();
-  sb.mat2x4_f32 = mat2x4<f32>();
-  sb.mat3x2_f32 = mat3x2<f32>();
-  sb.mat3x3_f32 = mat3x3<f32>();
-  sb.mat3x4_f32 = mat3x4<f32>();
-  sb.mat4x2_f32 = mat4x2<f32>();
-  sb.mat4x3_f32 = mat4x3<f32>();
-  sb.mat4x4_f32 = mat4x4<f32>();
-  sb.mat2x2_f16 = mat2x2<f16>();
-  sb.mat2x3_f16 = mat2x3<f16>();
-  sb.mat2x4_f16 = mat2x4<f16>();
-  sb.mat3x2_f16 = mat3x2<f16>();
-  sb.mat3x3_f16 = mat3x3<f16>();
-  sb.mat3x4_f16 = mat3x4<f16>();
-  sb.mat4x2_f16 = mat4x2<f16>();
-  sb.mat4x3_f16 = mat4x3<f16>();
-  sb.mat4x4_f16 = mat4x4<f16>();
-  sb.arr2_vec3_f32 = array<vec3<f32>, 2>();
-  sb.arr2_mat4x2_f16 = array<mat4x2<f16>, 2>();
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store(offset : u32, value : f32)
-
-@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_1(offset : u32, value : i32)
-
-@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_2(offset : u32, value : u32)
-
-@internal(intrinsic_store_storage_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_3(offset : u32, value : f16)
-
-@internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_4(offset : u32, value : vec2<f32>)
-
-@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_5(offset : u32, value : vec2<i32>)
-
-@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_6(offset : u32, value : vec2<u32>)
-
-@internal(intrinsic_store_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_7(offset : u32, value : vec2<f16>)
-
-@internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_8(offset : u32, value : vec3<f32>)
-
-@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_9(offset : u32, value : vec3<i32>)
-
-@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_10(offset : u32, value : vec3<u32>)
-
-@internal(intrinsic_store_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_11(offset : u32, value : vec3<f16>)
-
-@internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_12(offset : u32, value : vec4<f32>)
-
-@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_13(offset : u32, value : vec4<i32>)
-
-@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_14(offset : u32, value : vec4<u32>)
-
-@internal(intrinsic_store_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_15(offset : u32, value : vec4<f16>)
-
-fn sb_store_16(offset : u32, value : mat2x2<f32>) {
-  sb_store_4((offset + 0u), value[0u]);
-  sb_store_4((offset + 8u), value[1u]);
-}
-
-fn sb_store_17(offset : u32, value : mat2x3<f32>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 16u), value[1u]);
-}
-
-fn sb_store_18(offset : u32, value : mat2x4<f32>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 16u), value[1u]);
-}
-
-fn sb_store_19(offset : u32, value : mat3x2<f32>) {
-  sb_store_4((offset + 0u), value[0u]);
-  sb_store_4((offset + 8u), value[1u]);
-  sb_store_4((offset + 16u), value[2u]);
-}
-
-fn sb_store_20(offset : u32, value : mat3x3<f32>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 16u), value[1u]);
-  sb_store_8((offset + 32u), value[2u]);
-}
-
-fn sb_store_21(offset : u32, value : mat3x4<f32>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 16u), value[1u]);
-  sb_store_12((offset + 32u), value[2u]);
-}
-
-fn sb_store_22(offset : u32, value : mat4x2<f32>) {
-  sb_store_4((offset + 0u), value[0u]);
-  sb_store_4((offset + 8u), value[1u]);
-  sb_store_4((offset + 16u), value[2u]);
-  sb_store_4((offset + 24u), value[3u]);
-}
-
-fn sb_store_23(offset : u32, value : mat4x3<f32>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 16u), value[1u]);
-  sb_store_8((offset + 32u), value[2u]);
-  sb_store_8((offset + 48u), value[3u]);
-}
-
-fn sb_store_24(offset : u32, value : mat4x4<f32>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 16u), value[1u]);
-  sb_store_12((offset + 32u), value[2u]);
-  sb_store_12((offset + 48u), value[3u]);
-}
-
-fn sb_store_25(offset : u32, value : mat2x2<f16>) {
-  sb_store_7((offset + 0u), value[0u]);
-  sb_store_7((offset + 4u), value[1u]);
-}
-
-fn sb_store_26(offset : u32, value : mat2x3<f16>) {
-  sb_store_11((offset + 0u), value[0u]);
-  sb_store_11((offset + 8u), value[1u]);
-}
-
-fn sb_store_27(offset : u32, value : mat2x4<f16>) {
-  sb_store_15((offset + 0u), value[0u]);
-  sb_store_15((offset + 8u), value[1u]);
-}
-
-fn sb_store_28(offset : u32, value : mat3x2<f16>) {
-  sb_store_7((offset + 0u), value[0u]);
-  sb_store_7((offset + 4u), value[1u]);
-  sb_store_7((offset + 8u), value[2u]);
-}
-
-fn sb_store_29(offset : u32, value : mat3x3<f16>) {
-  sb_store_11((offset + 0u), value[0u]);
-  sb_store_11((offset + 8u), value[1u]);
-  sb_store_11((offset + 16u), value[2u]);
-}
-
-fn sb_store_30(offset : u32, value : mat3x4<f16>) {
-  sb_store_15((offset + 0u), value[0u]);
-  sb_store_15((offset + 8u), value[1u]);
-  sb_store_15((offset + 16u), value[2u]);
-}
-
-fn sb_store_31(offset : u32, value : mat4x2<f16>) {
-  sb_store_7((offset + 0u), value[0u]);
-  sb_store_7((offset + 4u), value[1u]);
-  sb_store_7((offset + 8u), value[2u]);
-  sb_store_7((offset + 12u), value[3u]);
-}
-
-fn sb_store_32(offset : u32, value : mat4x3<f16>) {
-  sb_store_11((offset + 0u), value[0u]);
-  sb_store_11((offset + 8u), value[1u]);
-  sb_store_11((offset + 16u), value[2u]);
-  sb_store_11((offset + 24u), value[3u]);
-}
-
-fn sb_store_33(offset : u32, value : mat4x4<f16>) {
-  sb_store_15((offset + 0u), value[0u]);
-  sb_store_15((offset + 8u), value[1u]);
-  sb_store_15((offset + 16u), value[2u]);
-  sb_store_15((offset + 24u), value[3u]);
-}
-
-fn sb_store_34(offset : u32, value : array<vec3<f32>, 2u>) {
-  var array_1 = value;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    sb_store_8((offset + (i * 16u)), array_1[i]);
-  }
-}
-
-fn sb_store_35(offset : u32, value : array<mat4x2<f16>, 2u>) {
-  var array_2 = value;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    sb_store_31((offset + (i_1 * 16u)), array_2[i_1]);
-  }
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  sb_store(0u, f32());
-  sb_store_1(4u, i32());
-  sb_store_2(8u, u32());
-  sb_store_3(12u, f16());
-  sb_store_4(16u, vec2<f32>());
-  sb_store_5(24u, vec2<i32>());
-  sb_store_6(32u, vec2<u32>());
-  sb_store_7(40u, vec2<f16>());
-  sb_store_8(48u, vec3<f32>());
-  sb_store_9(64u, vec3<i32>());
-  sb_store_10(80u, vec3<u32>());
-  sb_store_11(96u, vec3<f16>());
-  sb_store_12(112u, vec4<f32>());
-  sb_store_13(128u, vec4<i32>());
-  sb_store_14(144u, vec4<u32>());
-  sb_store_15(160u, vec4<f16>());
-  sb_store_16(168u, mat2x2<f32>());
-  sb_store_17(192u, mat2x3<f32>());
-  sb_store_18(224u, mat2x4<f32>());
-  sb_store_19(256u, mat3x2<f32>());
-  sb_store_20(288u, mat3x3<f32>());
-  sb_store_21(336u, mat3x4<f32>());
-  sb_store_22(384u, mat4x2<f32>());
-  sb_store_23(416u, mat4x3<f32>());
-  sb_store_24(480u, mat4x4<f32>());
-  sb_store_25(544u, mat2x2<f16>());
-  sb_store_26(552u, mat2x3<f16>());
-  sb_store_27(568u, mat2x4<f16>());
-  sb_store_28(584u, mat3x2<f16>());
-  sb_store_29(600u, mat3x3<f16>());
-  sb_store_30(624u, mat3x4<f16>());
-  sb_store_31(648u, mat4x2<f16>());
-  sb_store_32(664u, mat4x3<f16>());
-  sb_store_33(696u, mat4x4<f16>());
-  sb_store_34(736u, array<vec3<f32>, 2>());
-  sb_store_35(768u, array<mat4x2<f16>, 2>());
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, SB_BasicStore_OutOfOrder) {
-    auto* src = R"(
-enable f16;
-
-@compute @workgroup_size(1)
-fn main() {
-  sb.scalar_f32 = f32();
-  sb.scalar_i32 = i32();
-  sb.scalar_u32 = u32();
-  sb.scalar_f16 = f16();
-  sb.vec2_f32 = vec2<f32>();
-  sb.vec2_i32 = vec2<i32>();
-  sb.vec2_u32 = vec2<u32>();
-  sb.vec2_f16 = vec2<f16>();
-  sb.vec3_f32 = vec3<f32>();
-  sb.vec3_i32 = vec3<i32>();
-  sb.vec3_u32 = vec3<u32>();
-  sb.vec3_f16 = vec3<f16>();
-  sb.vec4_f32 = vec4<f32>();
-  sb.vec4_i32 = vec4<i32>();
-  sb.vec4_u32 = vec4<u32>();
-  sb.vec4_f16 = vec4<f16>();
-  sb.mat2x2_f32 = mat2x2<f32>();
-  sb.mat2x3_f32 = mat2x3<f32>();
-  sb.mat2x4_f32 = mat2x4<f32>();
-  sb.mat3x2_f32 = mat3x2<f32>();
-  sb.mat3x3_f32 = mat3x3<f32>();
-  sb.mat3x4_f32 = mat3x4<f32>();
-  sb.mat4x2_f32 = mat4x2<f32>();
-  sb.mat4x3_f32 = mat4x3<f32>();
-  sb.mat4x4_f32 = mat4x4<f32>();
-  sb.mat2x2_f16 = mat2x2<f16>();
-  sb.mat2x3_f16 = mat2x3<f16>();
-  sb.mat2x4_f16 = mat2x4<f16>();
-  sb.mat3x2_f16 = mat3x2<f16>();
-  sb.mat3x3_f16 = mat3x3<f16>();
-  sb.mat3x4_f16 = mat3x4<f16>();
-  sb.mat4x2_f16 = mat4x2<f16>();
-  sb.mat4x3_f16 = mat4x3<f16>();
-  sb.mat4x4_f16 = mat4x4<f16>();
-  sb.arr2_vec3_f32 = array<vec3<f32>, 2>();
-  sb.arr2_mat4x2_f16 = array<mat4x2<f16>, 2>();
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-};
-)";
-
-    auto* expect = R"(
-enable f16;
-
-@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store(offset : u32, value : f32)
-
-@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_1(offset : u32, value : i32)
-
-@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_2(offset : u32, value : u32)
-
-@internal(intrinsic_store_storage_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_3(offset : u32, value : f16)
-
-@internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_4(offset : u32, value : vec2<f32>)
-
-@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_5(offset : u32, value : vec2<i32>)
-
-@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_6(offset : u32, value : vec2<u32>)
-
-@internal(intrinsic_store_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_7(offset : u32, value : vec2<f16>)
-
-@internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_8(offset : u32, value : vec3<f32>)
-
-@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_9(offset : u32, value : vec3<i32>)
-
-@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_10(offset : u32, value : vec3<u32>)
-
-@internal(intrinsic_store_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_11(offset : u32, value : vec3<f16>)
-
-@internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_12(offset : u32, value : vec4<f32>)
-
-@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_13(offset : u32, value : vec4<i32>)
-
-@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_14(offset : u32, value : vec4<u32>)
-
-@internal(intrinsic_store_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_15(offset : u32, value : vec4<f16>)
-
-fn sb_store_16(offset : u32, value : mat2x2<f32>) {
-  sb_store_4((offset + 0u), value[0u]);
-  sb_store_4((offset + 8u), value[1u]);
-}
-
-fn sb_store_17(offset : u32, value : mat2x3<f32>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 16u), value[1u]);
-}
-
-fn sb_store_18(offset : u32, value : mat2x4<f32>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 16u), value[1u]);
-}
-
-fn sb_store_19(offset : u32, value : mat3x2<f32>) {
-  sb_store_4((offset + 0u), value[0u]);
-  sb_store_4((offset + 8u), value[1u]);
-  sb_store_4((offset + 16u), value[2u]);
-}
-
-fn sb_store_20(offset : u32, value : mat3x3<f32>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 16u), value[1u]);
-  sb_store_8((offset + 32u), value[2u]);
-}
-
-fn sb_store_21(offset : u32, value : mat3x4<f32>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 16u), value[1u]);
-  sb_store_12((offset + 32u), value[2u]);
-}
-
-fn sb_store_22(offset : u32, value : mat4x2<f32>) {
-  sb_store_4((offset + 0u), value[0u]);
-  sb_store_4((offset + 8u), value[1u]);
-  sb_store_4((offset + 16u), value[2u]);
-  sb_store_4((offset + 24u), value[3u]);
-}
-
-fn sb_store_23(offset : u32, value : mat4x3<f32>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 16u), value[1u]);
-  sb_store_8((offset + 32u), value[2u]);
-  sb_store_8((offset + 48u), value[3u]);
-}
-
-fn sb_store_24(offset : u32, value : mat4x4<f32>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 16u), value[1u]);
-  sb_store_12((offset + 32u), value[2u]);
-  sb_store_12((offset + 48u), value[3u]);
-}
-
-fn sb_store_25(offset : u32, value : mat2x2<f16>) {
-  sb_store_7((offset + 0u), value[0u]);
-  sb_store_7((offset + 4u), value[1u]);
-}
-
-fn sb_store_26(offset : u32, value : mat2x3<f16>) {
-  sb_store_11((offset + 0u), value[0u]);
-  sb_store_11((offset + 8u), value[1u]);
-}
-
-fn sb_store_27(offset : u32, value : mat2x4<f16>) {
-  sb_store_15((offset + 0u), value[0u]);
-  sb_store_15((offset + 8u), value[1u]);
-}
-
-fn sb_store_28(offset : u32, value : mat3x2<f16>) {
-  sb_store_7((offset + 0u), value[0u]);
-  sb_store_7((offset + 4u), value[1u]);
-  sb_store_7((offset + 8u), value[2u]);
-}
-
-fn sb_store_29(offset : u32, value : mat3x3<f16>) {
-  sb_store_11((offset + 0u), value[0u]);
-  sb_store_11((offset + 8u), value[1u]);
-  sb_store_11((offset + 16u), value[2u]);
-}
-
-fn sb_store_30(offset : u32, value : mat3x4<f16>) {
-  sb_store_15((offset + 0u), value[0u]);
-  sb_store_15((offset + 8u), value[1u]);
-  sb_store_15((offset + 16u), value[2u]);
-}
-
-fn sb_store_31(offset : u32, value : mat4x2<f16>) {
-  sb_store_7((offset + 0u), value[0u]);
-  sb_store_7((offset + 4u), value[1u]);
-  sb_store_7((offset + 8u), value[2u]);
-  sb_store_7((offset + 12u), value[3u]);
-}
-
-fn sb_store_32(offset : u32, value : mat4x3<f16>) {
-  sb_store_11((offset + 0u), value[0u]);
-  sb_store_11((offset + 8u), value[1u]);
-  sb_store_11((offset + 16u), value[2u]);
-  sb_store_11((offset + 24u), value[3u]);
-}
-
-fn sb_store_33(offset : u32, value : mat4x4<f16>) {
-  sb_store_15((offset + 0u), value[0u]);
-  sb_store_15((offset + 8u), value[1u]);
-  sb_store_15((offset + 16u), value[2u]);
-  sb_store_15((offset + 24u), value[3u]);
-}
-
-fn sb_store_34(offset : u32, value : array<vec3<f32>, 2u>) {
-  var array_1 = value;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    sb_store_8((offset + (i * 16u)), array_1[i]);
-  }
-}
-
-fn sb_store_35(offset : u32, value : array<mat4x2<f16>, 2u>) {
-  var array_2 = value;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    sb_store_31((offset + (i_1 * 16u)), array_2[i_1]);
-  }
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  sb_store(0u, f32());
-  sb_store_1(4u, i32());
-  sb_store_2(8u, u32());
-  sb_store_3(12u, f16());
-  sb_store_4(16u, vec2<f32>());
-  sb_store_5(24u, vec2<i32>());
-  sb_store_6(32u, vec2<u32>());
-  sb_store_7(40u, vec2<f16>());
-  sb_store_8(48u, vec3<f32>());
-  sb_store_9(64u, vec3<i32>());
-  sb_store_10(80u, vec3<u32>());
-  sb_store_11(96u, vec3<f16>());
-  sb_store_12(112u, vec4<f32>());
-  sb_store_13(128u, vec4<i32>());
-  sb_store_14(144u, vec4<u32>());
-  sb_store_15(160u, vec4<f16>());
-  sb_store_16(168u, mat2x2<f32>());
-  sb_store_17(192u, mat2x3<f32>());
-  sb_store_18(224u, mat2x4<f32>());
-  sb_store_19(256u, mat3x2<f32>());
-  sb_store_20(288u, mat3x3<f32>());
-  sb_store_21(336u, mat3x4<f32>());
-  sb_store_22(384u, mat4x2<f32>());
-  sb_store_23(416u, mat4x3<f32>());
-  sb_store_24(480u, mat4x4<f32>());
-  sb_store_25(544u, mat2x2<f16>());
-  sb_store_26(552u, mat2x3<f16>());
-  sb_store_27(568u, mat2x4<f16>());
-  sb_store_28(584u, mat3x2<f16>());
-  sb_store_29(600u, mat3x3<f16>());
-  sb_store_30(624u, mat3x4<f16>());
-  sb_store_31(648u, mat4x2<f16>());
-  sb_store_32(664u, mat4x3<f16>());
-  sb_store_33(696u, mat4x4<f16>());
-  sb_store_34(736u, array<vec3<f32>, 2>());
-  sb_store_35(768u, array<mat4x2<f16>, 2>());
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, LoadStructure) {
-    auto* src = R"(
-enable f16;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var x : SB = sb;
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_1(offset : u32) -> f32
-
-@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_2(offset : u32) -> i32
-
-@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_3(offset : u32) -> u32
-
-@internal(intrinsic_load_storage_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_4(offset : u32) -> f16
-
-@internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_5(offset : u32) -> vec2<f32>
-
-@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_6(offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_7(offset : u32) -> vec2<u32>
-
-@internal(intrinsic_load_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_8(offset : u32) -> vec2<f16>
-
-@internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_9(offset : u32) -> vec3<f32>
-
-@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_10(offset : u32) -> vec3<i32>
-
-@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_11(offset : u32) -> vec3<u32>
-
-@internal(intrinsic_load_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_12(offset : u32) -> vec3<f16>
-
-@internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_13(offset : u32) -> vec4<f32>
-
-@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_14(offset : u32) -> vec4<i32>
-
-@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_15(offset : u32) -> vec4<u32>
-
-@internal(intrinsic_load_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_16(offset : u32) -> vec4<f16>
-
-fn sb_load_17(offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(sb_load_5((offset + 0u)), sb_load_5((offset + 8u)));
-}
-
-fn sb_load_18(offset : u32) -> mat2x3<f32> {
-  return mat2x3<f32>(sb_load_9((offset + 0u)), sb_load_9((offset + 16u)));
-}
-
-fn sb_load_19(offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(sb_load_13((offset + 0u)), sb_load_13((offset + 16u)));
-}
-
-fn sb_load_20(offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(sb_load_5((offset + 0u)), sb_load_5((offset + 8u)), sb_load_5((offset + 16u)));
-}
-
-fn sb_load_21(offset : u32) -> mat3x3<f32> {
-  return mat3x3<f32>(sb_load_9((offset + 0u)), sb_load_9((offset + 16u)), sb_load_9((offset + 32u)));
-}
-
-fn sb_load_22(offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(sb_load_13((offset + 0u)), sb_load_13((offset + 16u)), sb_load_13((offset + 32u)));
-}
-
-fn sb_load_23(offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(sb_load_5((offset + 0u)), sb_load_5((offset + 8u)), sb_load_5((offset + 16u)), sb_load_5((offset + 24u)));
-}
-
-fn sb_load_24(offset : u32) -> mat4x3<f32> {
-  return mat4x3<f32>(sb_load_9((offset + 0u)), sb_load_9((offset + 16u)), sb_load_9((offset + 32u)), sb_load_9((offset + 48u)));
-}
-
-fn sb_load_25(offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(sb_load_13((offset + 0u)), sb_load_13((offset + 16u)), sb_load_13((offset + 32u)), sb_load_13((offset + 48u)));
-}
-
-fn sb_load_26(offset : u32) -> mat2x2<f16> {
-  return mat2x2<f16>(sb_load_8((offset + 0u)), sb_load_8((offset + 4u)));
-}
-
-fn sb_load_27(offset : u32) -> mat2x3<f16> {
-  return mat2x3<f16>(sb_load_12((offset + 0u)), sb_load_12((offset + 8u)));
-}
-
-fn sb_load_28(offset : u32) -> mat2x4<f16> {
-  return mat2x4<f16>(sb_load_16((offset + 0u)), sb_load_16((offset + 8u)));
-}
-
-fn sb_load_29(offset : u32) -> mat3x2<f16> {
-  return mat3x2<f16>(sb_load_8((offset + 0u)), sb_load_8((offset + 4u)), sb_load_8((offset + 8u)));
-}
-
-fn sb_load_30(offset : u32) -> mat3x3<f16> {
-  return mat3x3<f16>(sb_load_12((offset + 0u)), sb_load_12((offset + 8u)), sb_load_12((offset + 16u)));
-}
-
-fn sb_load_31(offset : u32) -> mat3x4<f16> {
-  return mat3x4<f16>(sb_load_16((offset + 0u)), sb_load_16((offset + 8u)), sb_load_16((offset + 16u)));
-}
-
-fn sb_load_32(offset : u32) -> mat4x2<f16> {
-  return mat4x2<f16>(sb_load_8((offset + 0u)), sb_load_8((offset + 4u)), sb_load_8((offset + 8u)), sb_load_8((offset + 12u)));
-}
-
-fn sb_load_33(offset : u32) -> mat4x3<f16> {
-  return mat4x3<f16>(sb_load_12((offset + 0u)), sb_load_12((offset + 8u)), sb_load_12((offset + 16u)), sb_load_12((offset + 24u)));
-}
-
-fn sb_load_34(offset : u32) -> mat4x4<f16> {
-  return mat4x4<f16>(sb_load_16((offset + 0u)), sb_load_16((offset + 8u)), sb_load_16((offset + 16u)), sb_load_16((offset + 24u)));
-}
-
-fn sb_load_35(offset : u32) -> array<vec3<f32>, 2u> {
-  var arr : array<vec3<f32>, 2u>;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    arr[i] = sb_load_9((offset + (i * 16u)));
-  }
-  return arr;
-}
-
-fn sb_load_36(offset : u32) -> array<mat4x2<f16>, 2u> {
-  var arr_1 : array<mat4x2<f16>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr_1[i_1] = sb_load_32((offset + (i_1 * 16u)));
-  }
-  return arr_1;
-}
-
-fn sb_load(offset : u32) -> SB {
-  return SB(sb_load_1((offset + 0u)), sb_load_2((offset + 4u)), sb_load_3((offset + 8u)), sb_load_4((offset + 12u)), sb_load_5((offset + 16u)), sb_load_6((offset + 24u)), sb_load_7((offset + 32u)), sb_load_8((offset + 40u)), sb_load_9((offset + 48u)), sb_load_10((offset + 64u)), sb_load_11((offset + 80u)), sb_load_12((offset + 96u)), sb_load_13((offset + 112u)), sb_load_14((offset + 128u)), sb_load_15((offset + 144u)), sb_load_16((offset + 160u)), sb_load_17((offset + 168u)), sb_load_18((offset + 192u)), sb_load_19((offset + 224u)), sb_load_20((offset + 256u)), sb_load_21((offset + 288u)), sb_load_22((offset + 336u)), sb_load_23((offset + 384u)), sb_load_24((offset + 416u)), sb_load_25((offset + 480u)), sb_load_26((offset + 544u)), sb_load_27((offset + 552u)), sb_load_28((offset + 568u)), sb_load_29((offset + 584u)), sb_load_30((offset + 600u)), sb_load_31((offset + 624u)), sb_load_32((offset + 648u)), sb_load_33((offset + 664u)), sb_load_34((offset + 696u)), sb_load_35((offset + 736u)), sb_load_36((offset + 768u)));
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var x : SB = sb_load(0u);
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, LoadStructure_OutOfOrder) {
-    auto* src = R"(
-enable f16;
-
-@compute @workgroup_size(1)
-fn main() {
-  var x : SB = sb;
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-};
-)";
-
-    auto* expect = R"(
-enable f16;
-
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_1(offset : u32) -> f32
-
-@internal(intrinsic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_2(offset : u32) -> i32
-
-@internal(intrinsic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_3(offset : u32) -> u32
-
-@internal(intrinsic_load_storage_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_4(offset : u32) -> f16
-
-@internal(intrinsic_load_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_5(offset : u32) -> vec2<f32>
-
-@internal(intrinsic_load_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_6(offset : u32) -> vec2<i32>
-
-@internal(intrinsic_load_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_7(offset : u32) -> vec2<u32>
-
-@internal(intrinsic_load_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_8(offset : u32) -> vec2<f16>
-
-@internal(intrinsic_load_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_9(offset : u32) -> vec3<f32>
-
-@internal(intrinsic_load_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_10(offset : u32) -> vec3<i32>
-
-@internal(intrinsic_load_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_11(offset : u32) -> vec3<u32>
-
-@internal(intrinsic_load_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_12(offset : u32) -> vec3<f16>
-
-@internal(intrinsic_load_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load_13(offset : u32) -> vec4<f32>
-
-@internal(intrinsic_load_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn sb_load_14(offset : u32) -> vec4<i32>
-
-@internal(intrinsic_load_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn sb_load_15(offset : u32) -> vec4<u32>
-
-@internal(intrinsic_load_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn sb_load_16(offset : u32) -> vec4<f16>
-
-fn sb_load_17(offset : u32) -> mat2x2<f32> {
-  return mat2x2<f32>(sb_load_5((offset + 0u)), sb_load_5((offset + 8u)));
-}
-
-fn sb_load_18(offset : u32) -> mat2x3<f32> {
-  return mat2x3<f32>(sb_load_9((offset + 0u)), sb_load_9((offset + 16u)));
-}
-
-fn sb_load_19(offset : u32) -> mat2x4<f32> {
-  return mat2x4<f32>(sb_load_13((offset + 0u)), sb_load_13((offset + 16u)));
-}
-
-fn sb_load_20(offset : u32) -> mat3x2<f32> {
-  return mat3x2<f32>(sb_load_5((offset + 0u)), sb_load_5((offset + 8u)), sb_load_5((offset + 16u)));
-}
-
-fn sb_load_21(offset : u32) -> mat3x3<f32> {
-  return mat3x3<f32>(sb_load_9((offset + 0u)), sb_load_9((offset + 16u)), sb_load_9((offset + 32u)));
-}
-
-fn sb_load_22(offset : u32) -> mat3x4<f32> {
-  return mat3x4<f32>(sb_load_13((offset + 0u)), sb_load_13((offset + 16u)), sb_load_13((offset + 32u)));
-}
-
-fn sb_load_23(offset : u32) -> mat4x2<f32> {
-  return mat4x2<f32>(sb_load_5((offset + 0u)), sb_load_5((offset + 8u)), sb_load_5((offset + 16u)), sb_load_5((offset + 24u)));
-}
-
-fn sb_load_24(offset : u32) -> mat4x3<f32> {
-  return mat4x3<f32>(sb_load_9((offset + 0u)), sb_load_9((offset + 16u)), sb_load_9((offset + 32u)), sb_load_9((offset + 48u)));
-}
-
-fn sb_load_25(offset : u32) -> mat4x4<f32> {
-  return mat4x4<f32>(sb_load_13((offset + 0u)), sb_load_13((offset + 16u)), sb_load_13((offset + 32u)), sb_load_13((offset + 48u)));
-}
-
-fn sb_load_26(offset : u32) -> mat2x2<f16> {
-  return mat2x2<f16>(sb_load_8((offset + 0u)), sb_load_8((offset + 4u)));
-}
-
-fn sb_load_27(offset : u32) -> mat2x3<f16> {
-  return mat2x3<f16>(sb_load_12((offset + 0u)), sb_load_12((offset + 8u)));
-}
-
-fn sb_load_28(offset : u32) -> mat2x4<f16> {
-  return mat2x4<f16>(sb_load_16((offset + 0u)), sb_load_16((offset + 8u)));
-}
-
-fn sb_load_29(offset : u32) -> mat3x2<f16> {
-  return mat3x2<f16>(sb_load_8((offset + 0u)), sb_load_8((offset + 4u)), sb_load_8((offset + 8u)));
-}
-
-fn sb_load_30(offset : u32) -> mat3x3<f16> {
-  return mat3x3<f16>(sb_load_12((offset + 0u)), sb_load_12((offset + 8u)), sb_load_12((offset + 16u)));
-}
-
-fn sb_load_31(offset : u32) -> mat3x4<f16> {
-  return mat3x4<f16>(sb_load_16((offset + 0u)), sb_load_16((offset + 8u)), sb_load_16((offset + 16u)));
-}
-
-fn sb_load_32(offset : u32) -> mat4x2<f16> {
-  return mat4x2<f16>(sb_load_8((offset + 0u)), sb_load_8((offset + 4u)), sb_load_8((offset + 8u)), sb_load_8((offset + 12u)));
-}
-
-fn sb_load_33(offset : u32) -> mat4x3<f16> {
-  return mat4x3<f16>(sb_load_12((offset + 0u)), sb_load_12((offset + 8u)), sb_load_12((offset + 16u)), sb_load_12((offset + 24u)));
-}
-
-fn sb_load_34(offset : u32) -> mat4x4<f16> {
-  return mat4x4<f16>(sb_load_16((offset + 0u)), sb_load_16((offset + 8u)), sb_load_16((offset + 16u)), sb_load_16((offset + 24u)));
-}
-
-fn sb_load_35(offset : u32) -> array<vec3<f32>, 2u> {
-  var arr : array<vec3<f32>, 2u>;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    arr[i] = sb_load_9((offset + (i * 16u)));
-  }
-  return arr;
-}
-
-fn sb_load_36(offset : u32) -> array<mat4x2<f16>, 2u> {
-  var arr_1 : array<mat4x2<f16>, 2u>;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    arr_1[i_1] = sb_load_32((offset + (i_1 * 16u)));
-  }
-  return arr_1;
-}
-
-fn sb_load(offset : u32) -> SB {
-  return SB(sb_load_1((offset + 0u)), sb_load_2((offset + 4u)), sb_load_3((offset + 8u)), sb_load_4((offset + 12u)), sb_load_5((offset + 16u)), sb_load_6((offset + 24u)), sb_load_7((offset + 32u)), sb_load_8((offset + 40u)), sb_load_9((offset + 48u)), sb_load_10((offset + 64u)), sb_load_11((offset + 80u)), sb_load_12((offset + 96u)), sb_load_13((offset + 112u)), sb_load_14((offset + 128u)), sb_load_15((offset + 144u)), sb_load_16((offset + 160u)), sb_load_17((offset + 168u)), sb_load_18((offset + 192u)), sb_load_19((offset + 224u)), sb_load_20((offset + 256u)), sb_load_21((offset + 288u)), sb_load_22((offset + 336u)), sb_load_23((offset + 384u)), sb_load_24((offset + 416u)), sb_load_25((offset + 480u)), sb_load_26((offset + 544u)), sb_load_27((offset + 552u)), sb_load_28((offset + 568u)), sb_load_29((offset + 584u)), sb_load_30((offset + 600u)), sb_load_31((offset + 624u)), sb_load_32((offset + 648u)), sb_load_33((offset + 664u)), sb_load_34((offset + 696u)), sb_load_35((offset + 736u)), sb_load_36((offset + 768u)));
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var x : SB = sb_load(0u);
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, StoreStructure) {
-    auto* src = R"(
-enable f16;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  sb = SB();
-}
-)";
-
-    auto* expect = R"(
-enable f16;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_1(offset : u32, value : f32)
-
-@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_2(offset : u32, value : i32)
-
-@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_3(offset : u32, value : u32)
-
-@internal(intrinsic_store_storage_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_4(offset : u32, value : f16)
-
-@internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_5(offset : u32, value : vec2<f32>)
-
-@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_6(offset : u32, value : vec2<i32>)
-
-@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_7(offset : u32, value : vec2<u32>)
-
-@internal(intrinsic_store_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_8(offset : u32, value : vec2<f16>)
-
-@internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_9(offset : u32, value : vec3<f32>)
-
-@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_10(offset : u32, value : vec3<i32>)
-
-@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_11(offset : u32, value : vec3<u32>)
-
-@internal(intrinsic_store_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_12(offset : u32, value : vec3<f16>)
-
-@internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_13(offset : u32, value : vec4<f32>)
-
-@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_14(offset : u32, value : vec4<i32>)
-
-@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_15(offset : u32, value : vec4<u32>)
-
-@internal(intrinsic_store_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_16(offset : u32, value : vec4<f16>)
-
-fn sb_store_17(offset : u32, value : mat2x2<f32>) {
-  sb_store_5((offset + 0u), value[0u]);
-  sb_store_5((offset + 8u), value[1u]);
-}
-
-fn sb_store_18(offset : u32, value : mat2x3<f32>) {
-  sb_store_9((offset + 0u), value[0u]);
-  sb_store_9((offset + 16u), value[1u]);
-}
-
-fn sb_store_19(offset : u32, value : mat2x4<f32>) {
-  sb_store_13((offset + 0u), value[0u]);
-  sb_store_13((offset + 16u), value[1u]);
-}
-
-fn sb_store_20(offset : u32, value : mat3x2<f32>) {
-  sb_store_5((offset + 0u), value[0u]);
-  sb_store_5((offset + 8u), value[1u]);
-  sb_store_5((offset + 16u), value[2u]);
-}
-
-fn sb_store_21(offset : u32, value : mat3x3<f32>) {
-  sb_store_9((offset + 0u), value[0u]);
-  sb_store_9((offset + 16u), value[1u]);
-  sb_store_9((offset + 32u), value[2u]);
-}
-
-fn sb_store_22(offset : u32, value : mat3x4<f32>) {
-  sb_store_13((offset + 0u), value[0u]);
-  sb_store_13((offset + 16u), value[1u]);
-  sb_store_13((offset + 32u), value[2u]);
-}
-
-fn sb_store_23(offset : u32, value : mat4x2<f32>) {
-  sb_store_5((offset + 0u), value[0u]);
-  sb_store_5((offset + 8u), value[1u]);
-  sb_store_5((offset + 16u), value[2u]);
-  sb_store_5((offset + 24u), value[3u]);
-}
-
-fn sb_store_24(offset : u32, value : mat4x3<f32>) {
-  sb_store_9((offset + 0u), value[0u]);
-  sb_store_9((offset + 16u), value[1u]);
-  sb_store_9((offset + 32u), value[2u]);
-  sb_store_9((offset + 48u), value[3u]);
-}
-
-fn sb_store_25(offset : u32, value : mat4x4<f32>) {
-  sb_store_13((offset + 0u), value[0u]);
-  sb_store_13((offset + 16u), value[1u]);
-  sb_store_13((offset + 32u), value[2u]);
-  sb_store_13((offset + 48u), value[3u]);
-}
-
-fn sb_store_26(offset : u32, value : mat2x2<f16>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 4u), value[1u]);
-}
-
-fn sb_store_27(offset : u32, value : mat2x3<f16>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 8u), value[1u]);
-}
-
-fn sb_store_28(offset : u32, value : mat2x4<f16>) {
-  sb_store_16((offset + 0u), value[0u]);
-  sb_store_16((offset + 8u), value[1u]);
-}
-
-fn sb_store_29(offset : u32, value : mat3x2<f16>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 4u), value[1u]);
-  sb_store_8((offset + 8u), value[2u]);
-}
-
-fn sb_store_30(offset : u32, value : mat3x3<f16>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 8u), value[1u]);
-  sb_store_12((offset + 16u), value[2u]);
-}
-
-fn sb_store_31(offset : u32, value : mat3x4<f16>) {
-  sb_store_16((offset + 0u), value[0u]);
-  sb_store_16((offset + 8u), value[1u]);
-  sb_store_16((offset + 16u), value[2u]);
-}
-
-fn sb_store_32(offset : u32, value : mat4x2<f16>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 4u), value[1u]);
-  sb_store_8((offset + 8u), value[2u]);
-  sb_store_8((offset + 12u), value[3u]);
-}
-
-fn sb_store_33(offset : u32, value : mat4x3<f16>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 8u), value[1u]);
-  sb_store_12((offset + 16u), value[2u]);
-  sb_store_12((offset + 24u), value[3u]);
-}
-
-fn sb_store_34(offset : u32, value : mat4x4<f16>) {
-  sb_store_16((offset + 0u), value[0u]);
-  sb_store_16((offset + 8u), value[1u]);
-  sb_store_16((offset + 16u), value[2u]);
-  sb_store_16((offset + 24u), value[3u]);
-}
-
-fn sb_store_35(offset : u32, value : array<vec3<f32>, 2u>) {
-  var array_1 = value;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    sb_store_9((offset + (i * 16u)), array_1[i]);
-  }
-}
-
-fn sb_store_36(offset : u32, value : array<mat4x2<f16>, 2u>) {
-  var array_2 = value;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    sb_store_32((offset + (i_1 * 16u)), array_2[i_1]);
-  }
-}
-
-fn sb_store(offset : u32, value : SB) {
-  sb_store_1((offset + 0u), value.scalar_f32);
-  sb_store_2((offset + 4u), value.scalar_i32);
-  sb_store_3((offset + 8u), value.scalar_u32);
-  sb_store_4((offset + 12u), value.scalar_f16);
-  sb_store_5((offset + 16u), value.vec2_f32);
-  sb_store_6((offset + 24u), value.vec2_i32);
-  sb_store_7((offset + 32u), value.vec2_u32);
-  sb_store_8((offset + 40u), value.vec2_f16);
-  sb_store_9((offset + 48u), value.vec3_f32);
-  sb_store_10((offset + 64u), value.vec3_i32);
-  sb_store_11((offset + 80u), value.vec3_u32);
-  sb_store_12((offset + 96u), value.vec3_f16);
-  sb_store_13((offset + 112u), value.vec4_f32);
-  sb_store_14((offset + 128u), value.vec4_i32);
-  sb_store_15((offset + 144u), value.vec4_u32);
-  sb_store_16((offset + 160u), value.vec4_f16);
-  sb_store_17((offset + 168u), value.mat2x2_f32);
-  sb_store_18((offset + 192u), value.mat2x3_f32);
-  sb_store_19((offset + 224u), value.mat2x4_f32);
-  sb_store_20((offset + 256u), value.mat3x2_f32);
-  sb_store_21((offset + 288u), value.mat3x3_f32);
-  sb_store_22((offset + 336u), value.mat3x4_f32);
-  sb_store_23((offset + 384u), value.mat4x2_f32);
-  sb_store_24((offset + 416u), value.mat4x3_f32);
-  sb_store_25((offset + 480u), value.mat4x4_f32);
-  sb_store_26((offset + 544u), value.mat2x2_f16);
-  sb_store_27((offset + 552u), value.mat2x3_f16);
-  sb_store_28((offset + 568u), value.mat2x4_f16);
-  sb_store_29((offset + 584u), value.mat3x2_f16);
-  sb_store_30((offset + 600u), value.mat3x3_f16);
-  sb_store_31((offset + 624u), value.mat3x4_f16);
-  sb_store_32((offset + 648u), value.mat4x2_f16);
-  sb_store_33((offset + 664u), value.mat4x3_f16);
-  sb_store_34((offset + 696u), value.mat4x4_f16);
-  sb_store_35((offset + 736u), value.arr2_vec3_f32);
-  sb_store_36((offset + 768u), value.arr2_mat4x2_f16);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  sb_store(0u, SB());
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, StoreStructure_OutOfOrder) {
-    auto* src = R"(
-enable f16;
-
-@compute @workgroup_size(1)
-fn main() {
-  sb = SB();
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-};
-)";
-
-    auto* expect = R"(
-enable f16;
-
-@internal(intrinsic_store_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_1(offset : u32, value : f32)
-
-@internal(intrinsic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_2(offset : u32, value : i32)
-
-@internal(intrinsic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_3(offset : u32, value : u32)
-
-@internal(intrinsic_store_storage_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_4(offset : u32, value : f16)
-
-@internal(intrinsic_store_storage_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_5(offset : u32, value : vec2<f32>)
-
-@internal(intrinsic_store_storage_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_6(offset : u32, value : vec2<i32>)
-
-@internal(intrinsic_store_storage_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_7(offset : u32, value : vec2<u32>)
-
-@internal(intrinsic_store_storage_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_8(offset : u32, value : vec2<f16>)
-
-@internal(intrinsic_store_storage_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_9(offset : u32, value : vec3<f32>)
-
-@internal(intrinsic_store_storage_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_10(offset : u32, value : vec3<i32>)
-
-@internal(intrinsic_store_storage_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_11(offset : u32, value : vec3<u32>)
-
-@internal(intrinsic_store_storage_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_12(offset : u32, value : vec3<f16>)
-
-@internal(intrinsic_store_storage_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn sb_store_13(offset : u32, value : vec4<f32>)
-
-@internal(intrinsic_store_storage_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn sb_store_14(offset : u32, value : vec4<i32>)
-
-@internal(intrinsic_store_storage_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn sb_store_15(offset : u32, value : vec4<u32>)
-
-@internal(intrinsic_store_storage_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn sb_store_16(offset : u32, value : vec4<f16>)
-
-fn sb_store_17(offset : u32, value : mat2x2<f32>) {
-  sb_store_5((offset + 0u), value[0u]);
-  sb_store_5((offset + 8u), value[1u]);
-}
-
-fn sb_store_18(offset : u32, value : mat2x3<f32>) {
-  sb_store_9((offset + 0u), value[0u]);
-  sb_store_9((offset + 16u), value[1u]);
-}
-
-fn sb_store_19(offset : u32, value : mat2x4<f32>) {
-  sb_store_13((offset + 0u), value[0u]);
-  sb_store_13((offset + 16u), value[1u]);
-}
-
-fn sb_store_20(offset : u32, value : mat3x2<f32>) {
-  sb_store_5((offset + 0u), value[0u]);
-  sb_store_5((offset + 8u), value[1u]);
-  sb_store_5((offset + 16u), value[2u]);
-}
-
-fn sb_store_21(offset : u32, value : mat3x3<f32>) {
-  sb_store_9((offset + 0u), value[0u]);
-  sb_store_9((offset + 16u), value[1u]);
-  sb_store_9((offset + 32u), value[2u]);
-}
-
-fn sb_store_22(offset : u32, value : mat3x4<f32>) {
-  sb_store_13((offset + 0u), value[0u]);
-  sb_store_13((offset + 16u), value[1u]);
-  sb_store_13((offset + 32u), value[2u]);
-}
-
-fn sb_store_23(offset : u32, value : mat4x2<f32>) {
-  sb_store_5((offset + 0u), value[0u]);
-  sb_store_5((offset + 8u), value[1u]);
-  sb_store_5((offset + 16u), value[2u]);
-  sb_store_5((offset + 24u), value[3u]);
-}
-
-fn sb_store_24(offset : u32, value : mat4x3<f32>) {
-  sb_store_9((offset + 0u), value[0u]);
-  sb_store_9((offset + 16u), value[1u]);
-  sb_store_9((offset + 32u), value[2u]);
-  sb_store_9((offset + 48u), value[3u]);
-}
-
-fn sb_store_25(offset : u32, value : mat4x4<f32>) {
-  sb_store_13((offset + 0u), value[0u]);
-  sb_store_13((offset + 16u), value[1u]);
-  sb_store_13((offset + 32u), value[2u]);
-  sb_store_13((offset + 48u), value[3u]);
-}
-
-fn sb_store_26(offset : u32, value : mat2x2<f16>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 4u), value[1u]);
-}
-
-fn sb_store_27(offset : u32, value : mat2x3<f16>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 8u), value[1u]);
-}
-
-fn sb_store_28(offset : u32, value : mat2x4<f16>) {
-  sb_store_16((offset + 0u), value[0u]);
-  sb_store_16((offset + 8u), value[1u]);
-}
-
-fn sb_store_29(offset : u32, value : mat3x2<f16>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 4u), value[1u]);
-  sb_store_8((offset + 8u), value[2u]);
-}
-
-fn sb_store_30(offset : u32, value : mat3x3<f16>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 8u), value[1u]);
-  sb_store_12((offset + 16u), value[2u]);
-}
-
-fn sb_store_31(offset : u32, value : mat3x4<f16>) {
-  sb_store_16((offset + 0u), value[0u]);
-  sb_store_16((offset + 8u), value[1u]);
-  sb_store_16((offset + 16u), value[2u]);
-}
-
-fn sb_store_32(offset : u32, value : mat4x2<f16>) {
-  sb_store_8((offset + 0u), value[0u]);
-  sb_store_8((offset + 4u), value[1u]);
-  sb_store_8((offset + 8u), value[2u]);
-  sb_store_8((offset + 12u), value[3u]);
-}
-
-fn sb_store_33(offset : u32, value : mat4x3<f16>) {
-  sb_store_12((offset + 0u), value[0u]);
-  sb_store_12((offset + 8u), value[1u]);
-  sb_store_12((offset + 16u), value[2u]);
-  sb_store_12((offset + 24u), value[3u]);
-}
-
-fn sb_store_34(offset : u32, value : mat4x4<f16>) {
-  sb_store_16((offset + 0u), value[0u]);
-  sb_store_16((offset + 8u), value[1u]);
-  sb_store_16((offset + 16u), value[2u]);
-  sb_store_16((offset + 24u), value[3u]);
-}
-
-fn sb_store_35(offset : u32, value : array<vec3<f32>, 2u>) {
-  var array_1 = value;
-  for(var i = 0u; (i < 2u); i = (i + 1u)) {
-    sb_store_9((offset + (i * 16u)), array_1[i]);
-  }
-}
-
-fn sb_store_36(offset : u32, value : array<mat4x2<f16>, 2u>) {
-  var array_2 = value;
-  for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
-    sb_store_32((offset + (i_1 * 16u)), array_2[i_1]);
-  }
-}
-
-fn sb_store(offset : u32, value : SB) {
-  sb_store_1((offset + 0u), value.scalar_f32);
-  sb_store_2((offset + 4u), value.scalar_i32);
-  sb_store_3((offset + 8u), value.scalar_u32);
-  sb_store_4((offset + 12u), value.scalar_f16);
-  sb_store_5((offset + 16u), value.vec2_f32);
-  sb_store_6((offset + 24u), value.vec2_i32);
-  sb_store_7((offset + 32u), value.vec2_u32);
-  sb_store_8((offset + 40u), value.vec2_f16);
-  sb_store_9((offset + 48u), value.vec3_f32);
-  sb_store_10((offset + 64u), value.vec3_i32);
-  sb_store_11((offset + 80u), value.vec3_u32);
-  sb_store_12((offset + 96u), value.vec3_f16);
-  sb_store_13((offset + 112u), value.vec4_f32);
-  sb_store_14((offset + 128u), value.vec4_i32);
-  sb_store_15((offset + 144u), value.vec4_u32);
-  sb_store_16((offset + 160u), value.vec4_f16);
-  sb_store_17((offset + 168u), value.mat2x2_f32);
-  sb_store_18((offset + 192u), value.mat2x3_f32);
-  sb_store_19((offset + 224u), value.mat2x4_f32);
-  sb_store_20((offset + 256u), value.mat3x2_f32);
-  sb_store_21((offset + 288u), value.mat3x3_f32);
-  sb_store_22((offset + 336u), value.mat3x4_f32);
-  sb_store_23((offset + 384u), value.mat4x2_f32);
-  sb_store_24((offset + 416u), value.mat4x3_f32);
-  sb_store_25((offset + 480u), value.mat4x4_f32);
-  sb_store_26((offset + 544u), value.mat2x2_f16);
-  sb_store_27((offset + 552u), value.mat2x3_f16);
-  sb_store_28((offset + 568u), value.mat2x4_f16);
-  sb_store_29((offset + 584u), value.mat3x2_f16);
-  sb_store_30((offset + 600u), value.mat3x3_f16);
-  sb_store_31((offset + 624u), value.mat3x4_f16);
-  sb_store_32((offset + 648u), value.mat4x2_f16);
-  sb_store_33((offset + 664u), value.mat4x3_f16);
-  sb_store_34((offset + 696u), value.mat4x4_f16);
-  sb_store_35((offset + 736u), value.arr2_vec3_f32);
-  sb_store_36((offset + 768u), value.arr2_mat4x2_f16);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  sb_store(0u, SB());
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  scalar_f32 : f32,
-  scalar_i32 : i32,
-  scalar_u32 : u32,
-  scalar_f16 : f16,
-  vec2_f32 : vec2<f32>,
-  vec2_i32 : vec2<i32>,
-  vec2_u32 : vec2<u32>,
-  vec2_f16 : vec2<f16>,
-  vec3_f32 : vec3<f32>,
-  vec3_i32 : vec3<i32>,
-  vec3_u32 : vec3<u32>,
-  vec3_f16 : vec3<f16>,
-  vec4_f32 : vec4<f32>,
-  vec4_i32 : vec4<i32>,
-  vec4_u32 : vec4<u32>,
-  vec4_f16 : vec4<f16>,
-  mat2x2_f32 : mat2x2<f32>,
-  mat2x3_f32 : mat2x3<f32>,
-  mat2x4_f32 : mat2x4<f32>,
-  mat3x2_f32 : mat3x2<f32>,
-  mat3x3_f32 : mat3x3<f32>,
-  mat3x4_f32 : mat3x4<f32>,
-  mat4x2_f32 : mat4x2<f32>,
-  mat4x3_f32 : mat4x3<f32>,
-  mat4x4_f32 : mat4x4<f32>,
-  mat2x2_f16 : mat2x2<f16>,
-  mat2x3_f16 : mat2x3<f16>,
-  mat2x4_f16 : mat2x4<f16>,
-  mat3x2_f16 : mat3x2<f16>,
-  mat3x3_f16 : mat3x3<f16>,
-  mat3x4_f16 : mat3x4<f16>,
-  mat4x2_f16 : mat4x2<f16>,
-  mat4x3_f16 : mat4x3<f16>,
-  mat4x4_f16 : mat4x4<f16>,
-  arr2_vec3_f32 : array<vec3<f32>, 2>,
-  arr2_mat4x2_f16 : array<mat4x2<f16>, 2>,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ComplexStaticAccessChain) {
-    auto* src = R"(
-// sizeof(S1) == 32
-// alignof(S1) == 16
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-};
-
-// sizeof(S2) == 116
-// alignof(S2) == 16
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-};
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var x : f32 = sb.b[4].b[1].b.z;
-}
-)";
-
-    // sb.b[4].b[1].b.z
-    //    ^  ^ ^  ^ ^ ^
-    //    |  | |  | | |
-    //  128  | |688 | 712
-    //       | |    |
-    //     640 656  704
-
-    auto* expect = R"(
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-}
-
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-}
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@compute @workgroup_size(1)
-fn main() {
-  var x : f32 = sb_load(712u);
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ComplexStaticAccessChain_ViaPointerDot) {
-    auto* src = R"(
-// sizeof(S1) == 32
-// alignof(S1) == 16
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-};
-
-// sizeof(S2) == 116
-// alignof(S2) == 16
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-};
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  let p = &sb;
-  var x : f32 = (*p).b[4].b[1].b.z;
-}
-)";
-
-    // sb.b[4].b[1].b.z
-    //    ^  ^ ^  ^ ^ ^
-    //    |  | |  | | |
-    //  128  | |688 | 712
-    //       | |    |
-    //     640 656  704
-
-    auto* expect = R"(
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-}
-
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-}
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@compute @workgroup_size(1)
-fn main() {
-  var x : f32 = sb_load(712u);
-}
-)";
-
-    auto got = Run<ast::transform::SimplifyPointers, DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ComplexStaticAccessChain_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var x : f32 = sb.b[4].b[1].b.z;
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-};
-
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-};
-
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-};
-)";
-
-    // sb.b[4].b[1].b.z
-    //    ^  ^ ^  ^ ^ ^
-    //    |  | |  | | |
-    //  128  | |688 | 712
-    //       | |    |
-    //     640 656  704
-
-    auto* expect = R"(
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@compute @workgroup_size(1)
-fn main() {
-  var x : f32 = sb_load(712u);
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-}
-
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-}
-
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChain) {
-    auto* src = R"(
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-};
-
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-};
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var i : i32 = 4;
-  var j : u32 = 1u;
-  var k : i32 = 2;
-  var x : f32 = sb.b[i].b[j].b[k];
-}
-)";
-
-    auto* expect = R"(
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-}
-
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-}
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@compute @workgroup_size(1)
-fn main() {
-  var i : i32 = 4;
-  var j : u32 = 1u;
-  var k : i32 = 2;
-  var x : f32 = sb_load((((((128u + (128u * u32(i))) + 16u) + (32u * j)) + 16u) + (4u * u32(k))));
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChain_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var i : i32 = 4;
-  var j : u32 = 1u;
-  var k : i32 = 2;
-  var x : f32 = sb.b[i].b[j].b[k];
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>
-};
-
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-};
-
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-};
-)";
-
-    auto* expect = R"(
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@compute @workgroup_size(1)
-fn main() {
-  var i : i32 = 4;
-  var j : u32 = 1u;
-  var k : i32 = 2;
-  var x : f32 = sb_load((((((128u + (128u * u32(i))) + 16u) + (32u * j)) + 16u) + (4u * u32(k))));
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : array<S2>,
-}
-
-struct S2 {
-  a : i32,
-  b : array<S1, 3>,
-  c : i32,
-}
-
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChainWithAliases) {
-    auto* src = R"(
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-};
-
-alias A1 = S1;
-
-alias A1_Array = array<S1, 3>;
-
-struct S2 {
-  a : i32,
-  b : A1_Array,
-  c : i32,
-};
-
-alias A2 = S2;
-
-alias A2_Array = array<S2>;
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : A2_Array,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  var i : i32 = 4;
-  var j : u32 = 1u;
-  var k : i32 = 2;
-  var x : f32 = sb.b[i].b[j].b[k];
-}
-)";
-
-    auto* expect = R"(
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-}
-
-alias A1 = S1;
-
-alias A1_Array = array<S1, 3>;
-
-struct S2 {
-  a : i32,
-  b : A1_Array,
-  c : i32,
-}
-
-alias A2 = S2;
-
-alias A2_Array = array<S2>;
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : A2_Array,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@compute @workgroup_size(1)
-fn main() {
-  var i : i32 = 4;
-  var j : u32 = 1u;
-  var k : i32 = 2;
-  var x : f32 = sb_load((((((128u + (128u * u32(i))) + 16u) + (32u * j)) + 16u) + (4u * u32(k))));
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, ComplexDynamicAccessChainWithAliases_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var i : i32 = 4;
-  var j : u32 = 1u;
-  var k : i32 = 2;
-  var x : f32 = sb.b[i].b[j].b[k];
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : A2_Array,
-};
-
-alias A2_Array = array<S2>;
-
-alias A2 = S2;
-
-struct S2 {
-  a : i32,
-  b : A1_Array,
-  c : i32,
-};
-
-alias A1 = S1;
-
-alias A1_Array = array<S1, 3>;
-
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-};
-)";
-
-    auto* expect = R"(
-@internal(intrinsic_load_storage_f32) @internal(disable_validation__function_has_no_body)
-fn sb_load(offset : u32) -> f32
-
-@compute @workgroup_size(1)
-fn main() {
-  var i : i32 = 4;
-  var j : u32 = 1u;
-  var k : i32 = 2;
-  var x : f32 = sb_load((((((128u + (128u * u32(i))) + 16u) + (32u * j)) + 16u) + (4u * u32(k))));
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  @size(128)
-  a : i32,
-  b : A2_Array,
-}
-
-alias A2_Array = array<S2>;
-
-alias A2 = S2;
-
-struct S2 {
-  a : i32,
-  b : A1_Array,
-  c : i32,
-}
-
-alias A1 = S1;
-
-alias A1_Array = array<S1, 3>;
-
-struct S1 {
-  a : i32,
-  b : vec3<f32>,
-  c : i32,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, StorageBufferAtomics) {
-    auto* src = R"(
-struct SB {
-  padding : vec4<f32>,
-  a : atomic<i32>,
-  b : atomic<u32>,
-};
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@compute @workgroup_size(1)
-fn main() {
-  atomicStore(&sb.a, 123);
-  atomicLoad(&sb.a);
-  atomicAdd(&sb.a, 123);
-  atomicSub(&sb.a, 123);
-  atomicMax(&sb.a, 123);
-  atomicMin(&sb.a, 123);
-  atomicAnd(&sb.a, 123);
-  atomicOr(&sb.a, 123);
-  atomicXor(&sb.a, 123);
-  atomicExchange(&sb.a, 123);
-  atomicCompareExchangeWeak(&sb.a, 123, 345);
-
-  atomicStore(&sb.b, 123u);
-  atomicLoad(&sb.b);
-  atomicAdd(&sb.b, 123u);
-  atomicSub(&sb.b, 123u);
-  atomicMax(&sb.b, 123u);
-  atomicMin(&sb.b, 123u);
-  atomicAnd(&sb.b, 123u);
-  atomicOr(&sb.b, 123u);
-  atomicXor(&sb.b, 123u);
-  atomicExchange(&sb.b, 123u);
-  atomicCompareExchangeWeak(&sb.b, 123u, 345u);
-}
-)";
-
-    auto* expect = R"(
-struct SB {
-  padding : vec4<f32>,
-  a : atomic<i32>,
-  b : atomic<u32>,
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-@internal(intrinsic_atomic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicStore(offset : u32, param_1 : i32)
-
-@internal(intrinsic_atomic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicLoad(offset : u32) -> i32
-
-@internal(intrinsic_atomic_add_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicAdd(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_sub_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicSub(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_max_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicMax(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_min_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicMin(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_and_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicAnd(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_or_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicOr(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_xor_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicXor(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_exchange_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicExchange(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_compare_exchange_weak_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicCompareExchangeWeak(offset : u32, param_1 : i32, param_2 : i32) -> __atomic_compare_exchange_result_i32
-
-@internal(intrinsic_atomic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicStore_1(offset : u32, param_1 : u32)
-
-@internal(intrinsic_atomic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicLoad_1(offset : u32) -> u32
-
-@internal(intrinsic_atomic_add_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicAdd_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_sub_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicSub_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_max_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicMax_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_min_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicMin_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_and_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicAnd_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_or_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicOr_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_xor_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicXor_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_exchange_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicExchange_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_compare_exchange_weak_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicCompareExchangeWeak_1(offset : u32, param_1 : u32, param_2 : u32) -> __atomic_compare_exchange_result_u32
-
-@compute @workgroup_size(1)
-fn main() {
-  sbatomicStore(16u, 123);
-  sbatomicLoad(16u);
-  sbatomicAdd(16u, 123);
-  sbatomicSub(16u, 123);
-  sbatomicMax(16u, 123);
-  sbatomicMin(16u, 123);
-  sbatomicAnd(16u, 123);
-  sbatomicOr(16u, 123);
-  sbatomicXor(16u, 123);
-  sbatomicExchange(16u, 123);
-  sbatomicCompareExchangeWeak(16u, 123, 345);
-  sbatomicStore_1(20u, 123u);
-  sbatomicLoad_1(20u);
-  sbatomicAdd_1(20u, 123u);
-  sbatomicSub_1(20u, 123u);
-  sbatomicMax_1(20u, 123u);
-  sbatomicMin_1(20u, 123u);
-  sbatomicAnd_1(20u, 123u);
-  sbatomicOr_1(20u, 123u);
-  sbatomicXor_1(20u, 123u);
-  sbatomicExchange_1(20u, 123u);
-  sbatomicCompareExchangeWeak_1(20u, 123u, 345u);
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, StorageBufferAtomics_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  atomicStore(&sb.a, 123);
-  atomicLoad(&sb.a);
-  atomicAdd(&sb.a, 123);
-  atomicSub(&sb.a, 123);
-  atomicMax(&sb.a, 123);
-  atomicMin(&sb.a, 123);
-  atomicAnd(&sb.a, 123);
-  atomicOr(&sb.a, 123);
-  atomicXor(&sb.a, 123);
-  atomicExchange(&sb.a, 123);
-  atomicCompareExchangeWeak(&sb.a, 123, 345);
-
-  atomicStore(&sb.b, 123u);
-  atomicLoad(&sb.b);
-  atomicAdd(&sb.b, 123u);
-  atomicSub(&sb.b, 123u);
-  atomicMax(&sb.b, 123u);
-  atomicMin(&sb.b, 123u);
-  atomicAnd(&sb.b, 123u);
-  atomicOr(&sb.b, 123u);
-  atomicXor(&sb.b, 123u);
-  atomicExchange(&sb.b, 123u);
-  atomicCompareExchangeWeak(&sb.b, 123u, 345u);
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  padding : vec4<f32>,
-  a : atomic<i32>,
-  b : atomic<u32>,
-};
-)";
-
-    auto* expect = R"(
-@internal(intrinsic_atomic_store_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicStore(offset : u32, param_1 : i32)
-
-@internal(intrinsic_atomic_load_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicLoad(offset : u32) -> i32
-
-@internal(intrinsic_atomic_add_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicAdd(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_sub_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicSub(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_max_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicMax(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_min_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicMin(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_and_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicAnd(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_or_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicOr(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_xor_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicXor(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_exchange_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicExchange(offset : u32, param_1 : i32) -> i32
-
-@internal(intrinsic_atomic_compare_exchange_weak_storage_i32) @internal(disable_validation__function_has_no_body)
-fn sbatomicCompareExchangeWeak(offset : u32, param_1 : i32, param_2 : i32) -> __atomic_compare_exchange_result_i32
-
-@internal(intrinsic_atomic_store_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicStore_1(offset : u32, param_1 : u32)
-
-@internal(intrinsic_atomic_load_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicLoad_1(offset : u32) -> u32
-
-@internal(intrinsic_atomic_add_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicAdd_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_sub_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicSub_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_max_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicMax_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_min_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicMin_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_and_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicAnd_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_or_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicOr_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_xor_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicXor_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_exchange_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicExchange_1(offset : u32, param_1 : u32) -> u32
-
-@internal(intrinsic_atomic_compare_exchange_weak_storage_u32) @internal(disable_validation__function_has_no_body)
-fn sbatomicCompareExchangeWeak_1(offset : u32, param_1 : u32, param_2 : u32) -> __atomic_compare_exchange_result_u32
-
-@compute @workgroup_size(1)
-fn main() {
-  sbatomicStore(16u, 123);
-  sbatomicLoad(16u);
-  sbatomicAdd(16u, 123);
-  sbatomicSub(16u, 123);
-  sbatomicMax(16u, 123);
-  sbatomicMin(16u, 123);
-  sbatomicAnd(16u, 123);
-  sbatomicOr(16u, 123);
-  sbatomicXor(16u, 123);
-  sbatomicExchange(16u, 123);
-  sbatomicCompareExchangeWeak(16u, 123, 345);
-  sbatomicStore_1(20u, 123u);
-  sbatomicLoad_1(20u);
-  sbatomicAdd_1(20u, 123u);
-  sbatomicSub_1(20u, 123u);
-  sbatomicMax_1(20u, 123u);
-  sbatomicMin_1(20u, 123u);
-  sbatomicAnd_1(20u, 123u);
-  sbatomicOr_1(20u, 123u);
-  sbatomicXor_1(20u, 123u);
-  sbatomicExchange_1(20u, 123u);
-  sbatomicCompareExchangeWeak_1(20u, 123u, 345u);
-}
-
-@group(0) @binding(0) var<storage, read_write> sb : SB;
-
-struct SB {
-  padding : vec4<f32>,
-  a : atomic<i32>,
-  b : atomic<u32>,
-}
-)";
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, WorkgroupBufferAtomics) {
-    auto* src = R"(
-struct S {
-  padding : vec4<f32>,
-  a : atomic<i32>,
-  b : atomic<u32>,
-}
-
-var<workgroup> w : S;
-
-@compute @workgroup_size(1)
-fn main() {
-  atomicStore(&(w.a), 123);
-  atomicLoad(&(w.a));
-  atomicAdd(&(w.a), 123);
-  atomicSub(&(w.a), 123);
-  atomicMax(&(w.a), 123);
-  atomicMin(&(w.a), 123);
-  atomicAnd(&(w.a), 123);
-  atomicOr(&(w.a), 123);
-  atomicXor(&(w.a), 123);
-  atomicExchange(&(w.a), 123);
-  atomicCompareExchangeWeak(&(w.a), 123, 345);
-  atomicStore(&(w.b), 123u);
-  atomicLoad(&(w.b));
-  atomicAdd(&(w.b), 123u);
-  atomicSub(&(w.b), 123u);
-  atomicMax(&(w.b), 123u);
-  atomicMin(&(w.b), 123u);
-  atomicAnd(&(w.b), 123u);
-  atomicOr(&(w.b), 123u);
-  atomicXor(&(w.b), 123u);
-  atomicExchange(&(w.b), 123u);
-  atomicCompareExchangeWeak(&(w.b), 123u, 345u);
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(DecomposeMemoryAccessTest, WorkgroupBufferAtomics_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  atomicStore(&(w.a), 123);
-  atomicLoad(&(w.a));
-  atomicAdd(&(w.a), 123);
-  atomicSub(&(w.a), 123);
-  atomicMax(&(w.a), 123);
-  atomicMin(&(w.a), 123);
-  atomicAnd(&(w.a), 123);
-  atomicOr(&(w.a), 123);
-  atomicXor(&(w.a), 123);
-  atomicExchange(&(w.a), 123);
-  atomicCompareExchangeWeak(&(w.a), 123, 345);
-  atomicStore(&(w.b), 123u);
-  atomicLoad(&(w.b));
-  atomicAdd(&(w.b), 123u);
-  atomicSub(&(w.b), 123u);
-  atomicMax(&(w.b), 123u);
-  atomicMin(&(w.b), 123u);
-  atomicAnd(&(w.b), 123u);
-  atomicOr(&(w.b), 123u);
-  atomicXor(&(w.b), 123u);
-  atomicExchange(&(w.b), 123u);
-  atomicCompareExchangeWeak(&(w.b), 123u, 345u);
-}
-
-var<workgroup> w : S;
-
-struct S {
-  padding : vec4<f32>,
-  a : atomic<i32>,
-  b : atomic<u32>,
-}
-)";
-
-    auto* expect = src;
-
-    auto got = Run<DecomposeMemoryAccess>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.cc b/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.cc
deleted file mode 100644
index dd03321..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.cc
+++ /dev/null
@@ -1,239 +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/hlsl/writer/ast_raise/localize_struct_array_assignment.h"
-
-#include <unordered_map>
-#include <utility>
-
-#include "src/tint/lang/core/type/reference.h"
-#include "src/tint/lang/wgsl/ast/assignment_statement.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.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/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/value_expression.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/macros/scoped_assignment.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::LocalizeStructArrayAssignment);
-
-namespace tint::hlsl::writer {
-
-/// PIMPL state for the transform
-struct LocalizeStructArrayAssignment::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() {
-        struct Shared {
-            bool process_nested_nodes = false;
-            Vector<const ast::Statement*, 4> insert_before_stmts;
-            Vector<const ast::Statement*, 4> insert_after_stmts;
-        } s;
-
-        bool made_changes = false;
-
-        for (auto* node : src.ASTNodes().Objects()) {
-            if (auto* assign_stmt = node->As<ast::AssignmentStatement>()) {
-                // Process if it's an assignment statement to a dynamically indexed array
-                // within a struct on a function or private storage variable. This
-                // specific use-case is what FXC fails to compile with:
-                // error X3500: array reference cannot be used as an l-value; not natively
-                // addressable
-                if (!ContainsStructArrayIndex(assign_stmt->lhs)) {
-                    continue;
-                }
-                auto og = GetOriginatingTypeAndAddressSpace(assign_stmt);
-                if (!(og.first->Is<core::type::Struct>() &&
-                      (og.second == core::AddressSpace::kFunction ||
-                       og.second == core::AddressSpace::kPrivate))) {
-                    continue;
-                }
-
-                ctx.Replace(assign_stmt, [&, assign_stmt] {
-                    // Reset shared state for this assignment statement
-                    s = Shared{};
-
-                    const ast::Expression* new_lhs = nullptr;
-                    {
-                        TINT_SCOPED_ASSIGNMENT(s.process_nested_nodes, true);
-                        new_lhs = ctx.Clone(assign_stmt->lhs);
-                    }
-
-                    auto* new_assign_stmt = b.Assign(new_lhs, ctx.Clone(assign_stmt->rhs));
-
-                    // Combine insert_before_stmts + new_assign_stmt + insert_after_stmts into
-                    // a block and return it
-                    auto stmts = std::move(s.insert_before_stmts);
-                    stmts.Reserve(1 + s.insert_after_stmts.Length());
-                    stmts.Push(new_assign_stmt);
-                    for (auto* stmt : s.insert_after_stmts) {
-                        stmts.Push(stmt);
-                    }
-
-                    return b.Block(std::move(stmts));
-                });
-
-                made_changes = true;
-            }
-        }
-
-        if (!made_changes) {
-            return SkipTransform;
-        }
-
-        ctx.ReplaceAll(
-            [&](const ast::IndexAccessorExpression* index_access) -> const ast::Expression* {
-                if (!s.process_nested_nodes) {
-                    return nullptr;
-                }
-
-                // Indexing a member access expr?
-                auto* mem_access = index_access->object->As<ast::MemberAccessorExpression>();
-                if (!mem_access) {
-                    return nullptr;
-                }
-
-                // Process any nested IndexAccessorExpressions
-                mem_access = ctx.Clone(mem_access);
-
-                // Store the address of the member access into a let as we need to read
-                // the value twice e.g. let tint_symbol = &(s.a1);
-                auto mem_access_ptr = b.Sym();
-                s.insert_before_stmts.Push(b.Decl(b.Let(mem_access_ptr, b.AddressOf(mem_access))));
-
-                // Disable further transforms when cloning
-                TINT_SCOPED_ASSIGNMENT(s.process_nested_nodes, false);
-
-                // Copy entire array out of struct into local temp var
-                // e.g. var tint_symbol_1 = *(tint_symbol);
-                auto tmp_var = b.Sym();
-                s.insert_before_stmts.Push(b.Decl(b.Var(tmp_var, b.Deref(mem_access_ptr))));
-
-                // Replace input index_access with a clone of itself, but with its
-                // .object replaced by the new temp var. This is returned from this
-                // function to modify the original assignment statement. e.g.
-                // tint_symbol_1[uniforms.i]
-                auto* new_index_access = b.IndexAccessor(tmp_var, ctx.Clone(index_access->index));
-
-                // Assign temp var back to array
-                // e.g. *(tint_symbol) = tint_symbol_1;
-                auto* assign_rhs_to_temp = b.Assign(b.Deref(mem_access_ptr), tmp_var);
-                {
-                    Vector<const ast::Statement*, 8> stmts{assign_rhs_to_temp};
-                    for (auto* stmt : s.insert_after_stmts) {
-                        stmts.Push(stmt);
-                    }
-                    s.insert_after_stmts = std::move(stmts);
-                }
-
-                return new_index_access;
-            });
-
-        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};
-
-    /// Returns true if `expr` contains an index accessor expression to a
-    /// structure member of array type.
-    bool ContainsStructArrayIndex(const ast::Expression* expr) {
-        bool result = false;
-        TraverseExpressions(expr, [&](const ast::IndexAccessorExpression* ia) {
-            // Indexing using a runtime value?
-            auto* idx_sem = src.Sem().GetVal(ia->index);
-            if (!idx_sem->ConstantValue()) {
-                // Indexing a member access expr?
-                if (auto* ma = ia->object->As<ast::MemberAccessorExpression>()) {
-                    const auto* ma_ty = src.TypeOf(ma);
-                    if (DAWN_UNLIKELY(ma_ty->Is<core::type::Pointer>())) {
-                        TINT_ICE()
-                            << "lhs of index accessor expression should not be a pointer. These "
-                               "should have been removed by the SimplifyPointers transform";
-                    }
-                    // That accesses an array?
-                    if (ma_ty->UnwrapRef()->Is<core::type::Array>()) {
-                        result = true;
-                        return ast::TraverseAction::Stop;
-                    }
-                }
-            }
-            return ast::TraverseAction::Descend;
-        });
-
-        return result;
-    }
-
-    // Returns the type and address space of the originating variable of the lhs
-    // of the assignment statement.
-    // See https://www.w3.org/TR/WGSL/#originating-variable-section
-    std::pair<const core::type::Type*, core::AddressSpace> GetOriginatingTypeAndAddressSpace(
-        const ast::AssignmentStatement* assign_stmt) {
-        auto* root_ident = src.Sem().GetVal(assign_stmt->lhs)->RootIdentifier();
-        if (DAWN_UNLIKELY(!root_ident)) {
-            TINT_ICE() << "Unable to determine originating variable for lhs of assignment "
-                          "statement";
-        }
-
-        return Switch(
-            root_ident->Type(),  //
-            [&](const core::type::Reference* ref) {
-                return std::make_pair(ref->StoreType(), ref->AddressSpace());
-            },
-            [&](const core::type::Pointer* ptr) {
-                return std::make_pair(ptr->StoreType(), ptr->AddressSpace());
-            },  //
-            TINT_ICE_ON_NO_MATCH);
-    }
-};
-
-LocalizeStructArrayAssignment::LocalizeStructArrayAssignment() = default;
-
-LocalizeStructArrayAssignment::~LocalizeStructArrayAssignment() = default;
-
-ast::transform::Transform::ApplyResult LocalizeStructArrayAssignment::Apply(
-    const Program& src,
-    const ast::transform::DataMap&,
-    ast::transform::DataMap&) const {
-    return State{src}.Run();
-}
-
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.h b/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.h
deleted file mode 100644
index 550d625..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.h
+++ /dev/null
@@ -1,63 +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_HLSL_WRITER_AST_RAISE_LOCALIZE_STRUCT_ARRAY_ASSIGNMENT_H_
-#define SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_LOCALIZE_STRUCT_ARRAY_ASSIGNMENT_H_
-
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::hlsl::writer {
-
-/// This transforms replaces assignment to dynamically-indexed fixed-size arrays
-/// in structs on shader-local variables with code that copies the arrays to a
-/// temporary local variable, assigns to the local variable, and copies the
-/// array back. This is to work around FXC's compilation failure for these cases
-/// (see crbug.com/tint/1206).
-///
-/// @note Depends on the following transforms to have been run first:
-/// * SimplifyPointers
-class LocalizeStructArrayAssignment final
-    : public Castable<LocalizeStructArrayAssignment, ast::transform::Transform> {
-  public:
-    /// Constructor
-    LocalizeStructArrayAssignment();
-
-    /// Destructor
-    ~LocalizeStructArrayAssignment() 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::hlsl::writer
-
-#endif  // SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_LOCALIZE_STRUCT_ARRAY_ASSIGNMENT_H_
diff --git a/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment_test.cc b/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment_test.cc
deleted file mode 100644
index c6a6f6b..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment_test.cc
+++ /dev/null
@@ -1,1009 +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/hlsl/writer/ast_raise/localize_struct_array_assignment.h"
-#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
-#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using LocalizeStructArrayAssignmentTest = ast::transform::TransformTest;
-using Unshadow = ast::transform::Unshadow;
-using SimplifyPointers = ast::transform::SimplifyPointers;
-
-TEST_F(LocalizeStructArrayAssignmentTest, EmptyModule) {
-    auto* src = R"()";
-    EXPECT_FALSE(ShouldRun<LocalizeStructArrayAssignment>(src));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, StructArray) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  s1.a1[uniforms.i] = v;
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  {
-    let tint_symbol = &(s1.a1);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, StructArray_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  s1.a1[uniforms.i] = v;
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct Uniforms {
-  i : u32,
-};
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  {
-    let tint_symbol = &(s1.a1);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct Uniforms {
-  i : u32,
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, StructStructArray) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct S1 {
-  a : array<InnerS, 8>,
-};
-
-struct OuterS {
-  s2 : S1,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  s1.s2.a[uniforms.i] = v;
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct S1 {
-  a : array<InnerS, 8>,
-}
-
-struct OuterS {
-  s2 : S1,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  {
-    let tint_symbol = &(s1.s2.a);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, StructStructArray_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  s1.s2.a[uniforms.i] = v;
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-struct OuterS {
-  s2 : S1,
-};
-
-struct S1 {
-  a : array<InnerS, 8>,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct Uniforms {
-  i : u32,
-};
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  {
-    let tint_symbol = &(s1.s2.a);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-struct OuterS {
-  s2 : S1,
-}
-
-struct S1 {
-  a : array<InnerS, 8>,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct Uniforms {
-  i : u32,
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, StructArrayArray) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-  j : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct OuterS {
-  a1 : array<array<InnerS, 8>, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  s1.a1[uniforms.i][uniforms.j] = v;
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-  j : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct OuterS {
-  a1 : array<array<InnerS, 8>, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  {
-    let tint_symbol = &(s1.a1);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i][uniforms.j] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, StructArrayStruct) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct S1 {
-  s2 : InnerS,
-};
-
-struct OuterS {
-  a1 : array<S1, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  s1.a1[uniforms.i].s2 = v;
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct S1 {
-  s2 : InnerS,
-}
-
-struct OuterS {
-  a1 : array<S1, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  {
-    let tint_symbol = &(s1.a1);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i].s2 = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, StructArrayStructArray) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-  j : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct S1 {
-  a2 : array<InnerS, 8>,
-};
-
-struct OuterS {
-  a1 : array<S1, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s : OuterS;
-  s.a1[uniforms.i].a2[uniforms.j] = v;
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-  j : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct S1 {
-  a2 : array<InnerS, 8>,
-}
-
-struct OuterS {
-  a1 : array<S1, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s : OuterS;
-  {
-    let tint_symbol = &(s.a1);
-    var tint_symbol_1 = *(tint_symbol);
-    let tint_symbol_2 = &(tint_symbol_1[uniforms.i].a2);
-    var tint_symbol_3 = *(tint_symbol_2);
-    tint_symbol_3[uniforms.j] = v;
-    *(tint_symbol_2) = tint_symbol_3;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, IndexingWithSideEffectFunc) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-  j : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct S1 {
-  a2 : array<InnerS, 8>,
-};
-
-struct OuterS {
-  a1 : array<S1, 8>,
-};
-
-var<private> nextIndex : u32;
-fn getNextIndex() -> u32 {
-  nextIndex = nextIndex + 1u;
-  return nextIndex;
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s : OuterS;
-  s.a1[getNextIndex()].a2[uniforms.j] = v;
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-  j : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct S1 {
-  a2 : array<InnerS, 8>,
-}
-
-struct OuterS {
-  a1 : array<S1, 8>,
-}
-
-var<private> nextIndex : u32;
-
-fn getNextIndex() -> u32 {
-  nextIndex = (nextIndex + 1u);
-  return nextIndex;
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s : OuterS;
-  {
-    let tint_symbol = &(s.a1);
-    var tint_symbol_1 = *(tint_symbol);
-    let tint_symbol_2 = &(tint_symbol_1[getNextIndex()].a2);
-    var tint_symbol_3 = *(tint_symbol_2);
-    tint_symbol_3[uniforms.j] = v;
-    *(tint_symbol_2) = tint_symbol_3;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, IndexingWithSideEffectFunc_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s : OuterS;
-  s.a1[getNextIndex()].a2[uniforms.j] = v;
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-struct Uniforms {
-  i : u32,
-  j : u32,
-};
-
-var<private> nextIndex : u32;
-fn getNextIndex() -> u32 {
-  nextIndex = nextIndex + 1u;
-  return nextIndex;
-}
-
-struct OuterS {
-  a1 : array<S1, 8>,
-};
-
-struct S1 {
-  a2 : array<InnerS, 8>,
-};
-
-struct InnerS {
-  v : i32,
-};
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s : OuterS;
-  {
-    let tint_symbol = &(s.a1);
-    var tint_symbol_1 = *(tint_symbol);
-    let tint_symbol_2 = &(tint_symbol_1[getNextIndex()].a2);
-    var tint_symbol_3 = *(tint_symbol_2);
-    tint_symbol_3[uniforms.j] = v;
-    *(tint_symbol_2) = tint_symbol_3;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-struct Uniforms {
-  i : u32,
-  j : u32,
-}
-
-var<private> nextIndex : u32;
-
-fn getNextIndex() -> u32 {
-  nextIndex = (nextIndex + 1u);
-  return nextIndex;
-}
-
-struct OuterS {
-  a1 : array<S1, 8>,
-}
-
-struct S1 {
-  a2 : array<InnerS, 8>,
-}
-
-struct InnerS {
-  v : i32,
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerArg) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-struct InnerS {
-  v : i32,
-};
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-fn f(p : ptr<function, OuterS>) {
-  var v : InnerS;
-  (*p).a1[uniforms.i] = v;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var s1 : OuterS;
-  f(&s1);
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-fn f(p : ptr<function, OuterS>) {
-  var v : InnerS;
-  {
-    let tint_symbol = &((*(p)).a1);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var s1 : OuterS;
-  f(&(s1));
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerArg_PointerDot) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-struct InnerS {
-  v : i32,
-};
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-fn f(p : ptr<function, OuterS>) {
-  var v : InnerS;
-  p.a1[uniforms.i] = v;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var s1 : OuterS;
-  f(&s1);
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-fn f(p : ptr<function, OuterS>) {
-  var v : InnerS;
-  {
-    let tint_symbol = &((*(p)).a1);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var s1 : OuterS;
-  f(&(s1));
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerArg_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var s1 : OuterS;
-  f(&s1);
-}
-
-fn f(p : ptr<function, OuterS>) {
-  var v : InnerS;
-  (*p).a1[uniforms.i] = v;
-}
-
-struct InnerS {
-  v : i32,
-};
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-struct Uniforms {
-  i : u32,
-};
-)";
-
-    auto* expect = R"(
-@compute @workgroup_size(1)
-fn main() {
-  var s1 : OuterS;
-  f(&(s1));
-}
-
-fn f(p : ptr<function, OuterS>) {
-  var v : InnerS;
-  {
-    let tint_symbol = &((*(p)).a1);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[uniforms.i] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-struct Uniforms {
-  i : u32,
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, ViaPointerVar) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-fn f(p : ptr<function, InnerS>, v : InnerS) {
-  *(p) = v;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  let p = &(s1.a1[uniforms.i]);
-  *(p) = v;
-}
-)";
-
-    auto* expect = R"(
-struct Uniforms {
-  i : u32,
-}
-
-struct InnerS {
-  v : i32,
-}
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-fn f(p : ptr<function, InnerS>, v : InnerS) {
-  *(p) = v;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : OuterS;
-  let p_save = uniforms.i;
-  {
-    let tint_symbol = &(s1.a1);
-    var tint_symbol_1 = *(tint_symbol);
-    tint_symbol_1[p_save] = v;
-    *(tint_symbol) = tint_symbol_1;
-  }
-}
-)";
-
-    auto got = Run<Unshadow, SimplifyPointers, LocalizeStructArrayAssignment>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, VectorAssignment) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-}
-
-struct OuterS {
-  a1 : array<u32, 8>,
-}
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-fn f(i : u32) -> u32 {
-  return (i + 1u);
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  var s1 : OuterS;
-  var v : vec3<f32>;
-  v[s1.a1[uniforms.i]] = 1.0;
-  v[f(s1.a1[uniforms.i])] = 1.0;
-}
-)";
-
-    // Transform does nothing here as we're not actually assigning to the array in
-    // the struct.
-    EXPECT_FALSE(ShouldRun<LocalizeStructArrayAssignment>(src));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, ArrayStructArray) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : array<OuterS, 2>;
-  s1[uniforms.i].a1[uniforms.i] = v;
-}
-)";
-
-    // Transform does nothing as the struct-of-array is in an array, which FXC has no problem with.
-    EXPECT_FALSE(ShouldRun<LocalizeStructArrayAssignment>(src));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, ArrayStructArray_ViaPointerDerefIndex) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : array<OuterS, 2>;
-  let p = &s1;
-  (*p)[uniforms.i].a1[uniforms.i] = v;
-}
-)";
-
-    // Transform does nothing as the struct-of-array is in an array, which FXC has no problem with.
-    EXPECT_FALSE(ShouldRun<LocalizeStructArrayAssignment>(src));
-}
-
-TEST_F(LocalizeStructArrayAssignmentTest, ArrayStructArray_ViaPointerIndex) {
-    auto* src = R"(
-struct Uniforms {
-  i : u32,
-};
-
-struct InnerS {
-  v : i32,
-};
-
-struct OuterS {
-  a1 : array<InnerS, 8>,
-};
-
-@group(1) @binding(4) var<uniform> uniforms : Uniforms;
-
-@compute @workgroup_size(1)
-fn main() {
-  var v : InnerS;
-  var s1 : array<OuterS, 2>;
-  let p = &s1;
-  p[uniforms.i].a1[uniforms.i] = v;
-}
-)";
-
-    // Transform does nothing as the struct-of-array is in an array, which FXC has no problem with.
-    EXPECT_FALSE(ShouldRun<LocalizeStructArrayAssignment>(src));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.cc b/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.cc
deleted file mode 100644
index 161dad2..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.cc
+++ /dev/null
@@ -1,211 +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/hlsl/writer/ast_raise/num_workgroups_from_uniform.h"
-
-#include <memory>
-#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/transform/canonicalize_entry_point_io.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/math/hash.h"
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::NumWorkgroupsFromUniform);
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::NumWorkgroupsFromUniform::Config);
-
-namespace tint::hlsl::writer {
-namespace {
-
-bool ShouldRun(const Program& program) {
-    for (auto* node : program.ASTNodes().Objects()) {
-        if (auto* attr = node->As<ast::BuiltinAttribute>()) {
-            if (attr->builtin == core::BuiltinValue::kNumWorkgroups) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-/// Accessor describes the identifiers used in a member accessor that is being
-/// used to retrieve the num_workgroups builtin from a parameter.
-struct Accessor {
-    Symbol param;
-    Symbol member;
-
-    /// Equality operator
-    bool operator==(const Accessor& other) const {
-        return param == other.param && member == other.member;
-    }
-    /// Hash function
-    struct Hasher {
-        size_t operator()(const Accessor& a) const { return Hash(a.param, a.member); }
-    };
-};
-
-}  // namespace
-
-NumWorkgroupsFromUniform::NumWorkgroupsFromUniform() = default;
-NumWorkgroupsFromUniform::~NumWorkgroupsFromUniform() = default;
-
-ast::transform::Transform::ApplyResult NumWorkgroupsFromUniform::Apply(
-    const Program& src,
-    const ast::transform::DataMap& inputs,
-    ast::transform::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);
-    }
-
-    if (!ShouldRun(src)) {
-        return SkipTransform;
-    }
-
-    const char* kNumWorkgroupsMemberName = "num_workgroups";
-
-    // Find all entry point parameters that declare the num_workgroups builtin.
-    std::unordered_set<Accessor, Accessor::Hasher> to_replace;
-    for (auto* func : src.AST().Functions()) {
-        // num_workgroups is only valid for compute stages.
-        if (func->PipelineStage() != ast::PipelineStage::kCompute) {
-            continue;
-        }
-
-        for (auto* param : src.Sem().Get(func)->Parameters()) {
-            // Because the CanonicalizeEntryPointIO transform has been run, builtins
-            // will only appear as struct members.
-            auto* str = param->Type()->As<sem::Struct>();
-            if (!str) {
-                continue;
-            }
-
-            for (auto* member : str->Members()) {
-                if (member->Attributes().builtin != core::BuiltinValue::kNumWorkgroups) {
-                    continue;
-                }
-
-                // Capture the symbols that would be used to access this member, which
-                // we will replace later. We currently have no way to get from the
-                // parameter directly to the member accessor expressions that use it.
-                to_replace.insert({param->Declaration()->name->symbol, member->Name()});
-
-                // Remove the struct member.
-                // The CanonicalizeEntryPointIO transform will have generated this
-                // struct uniquely for this particular entry point, so we know that
-                // there will be no other uses of this struct in the module and that we
-                // can safely modify it here.
-                ctx.Remove(str->Declaration()->members, member->Declaration());
-
-                // If this is the only member, remove the struct and parameter too.
-                if (str->Members().Length() == 1) {
-                    ctx.Remove(func->params, param->Declaration());
-                    ctx.Remove(src.AST().GlobalDeclarations(), str->Declaration());
-                }
-            }
-        }
-    }
-
-    // Get (or create, on first call) the uniform buffer that will receive the
-    // number of workgroups.
-    const ast::Variable* num_workgroups_ubo = nullptr;
-    auto get_ubo = [&] {
-        if (!num_workgroups_ubo) {
-            auto* num_workgroups_struct =
-                b.Structure(b.Sym(), tint::Vector{
-                                         b.Member(kNumWorkgroupsMemberName, b.ty.vec3(b.ty.u32())),
-                                     });
-
-            uint32_t group, binding;
-            if (cfg->ubo_binding.has_value()) {
-                // If cfg->ubo_binding holds a value, use the specified binding point.
-                group = cfg->ubo_binding->group;
-                binding = cfg->ubo_binding->binding;
-            } else {
-                // If cfg->ubo_binding holds no value, use the binding 0 of the largest used group
-                // plus 1, or group 0 if no resource bound.
-                group = 0;
-
-                for (auto* global : src.AST().GlobalVariables()) {
-                    auto* global_sem = src.Sem().Get<sem::GlobalVariable>(global);
-                    if (auto bp = global_sem->Attributes().binding_point) {
-                        if (bp->group >= group) {
-                            group = bp->group + 1;
-                        }
-                    }
-                }
-
-                binding = 0;
-            }
-
-            num_workgroups_ubo =
-                b.GlobalVar(b.Sym(), b.ty.Of(num_workgroups_struct), core::AddressSpace::kUniform,
-                            b.Group(AInt(group)), b.Binding(AInt(binding)));
-        }
-        return num_workgroups_ubo;
-    };
-
-    // Now replace all the places where the builtins are accessed with the value
-    // loaded from the uniform buffer.
-    for (auto* node : src.ASTNodes().Objects()) {
-        auto* accessor = node->As<ast::MemberAccessorExpression>();
-        if (!accessor) {
-            continue;
-        }
-        auto* ident = accessor->object->As<ast::IdentifierExpression>();
-        if (!ident) {
-            continue;
-        }
-
-        if (to_replace.count({ident->identifier->symbol, accessor->member->symbol}) != 0u) {
-            ctx.Replace(accessor,
-                        b.MemberAccessor(get_ubo()->name->symbol, kNumWorkgroupsMemberName));
-        }
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-NumWorkgroupsFromUniform::Config::Config(std::optional<BindingPoint> ubo_bp)
-    : ubo_binding(ubo_bp) {}
-NumWorkgroupsFromUniform::Config::Config(const Config&) = default;
-NumWorkgroupsFromUniform::Config::~Config() = default;
-
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.h b/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.h
deleted file mode 100644
index eb5983a..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.h
+++ /dev/null
@@ -1,92 +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_HLSL_WRITER_AST_RAISE_NUM_WORKGROUPS_FROM_UNIFORM_H_
-#define SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_NUM_WORKGROUPS_FROM_UNIFORM_H_
-
-#include <optional>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::hlsl::writer {
-
-/// NumWorkgroupsFromUniform is a transform that implements the `num_workgroups`
-/// builtin by loading it from a uniform buffer.
-///
-/// The generated uniform buffer will have the form:
-/// ```
-/// struct num_workgroups_struct {
-///  num_workgroups : vec3<u32>;
-/// };
-///
-/// @group(0) @binding(0)
-/// var<uniform> num_workgroups_ubo : num_workgroups_struct;
-/// ```
-/// The binding group and number used for this uniform buffer is provided via
-/// the `Config` transform input.
-///
-/// @note Depends on the following transforms to have been run first:
-/// * CanonicalizeEntryPointIO
-class NumWorkgroupsFromUniform final
-    : public Castable<NumWorkgroupsFromUniform, ast::transform::Transform> {
-  public:
-    /// Constructor
-    NumWorkgroupsFromUniform();
-    /// Destructor
-    ~NumWorkgroupsFromUniform() override;
-
-    /// Configuration options for the NumWorkgroupsFromUniform transform.
-    struct Config final : public Castable<Config, ast::transform::Data> {
-        /// Constructor
-        /// @param ubo_bp the binding point to use for the generated uniform buffer. If ubo_bp
-        /// contains no value, a free binding point will be used to ensure the generated program is
-        /// valid. Specifically, binding 0 of the largest used group plus 1 is used if at least one
-        /// resource is bound, otherwise group 0 binding 0 is used.
-        explicit Config(std::optional<BindingPoint> ubo_bp);
-
-        /// Copy constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// The binding point to use for the generated uniform buffer. If ubo_bp contains no value,
-        /// a free binding point will be used. Specifically, binding 0 of the largest used group
-        /// plus 1 is used if at least one resource is bound, otherwise group 0 binding 0 is used.
-        std::optional<BindingPoint> ubo_binding;
-    };
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-};
-
-}  // namespace tint::hlsl::writer
-
-#endif  // SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_NUM_WORKGROUPS_FROM_UNIFORM_H_
diff --git a/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform_test.cc b/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform_test.cc
deleted file mode 100644
index ca580c0..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform_test.cc
+++ /dev/null
@@ -1,814 +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/hlsl/writer/ast_raise/num_workgroups_from_uniform.h"
-
-#include <utility>
-
-#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::hlsl::writer {
-namespace {
-
-using NumWorkgroupsFromUniformTest = ast::transform::TransformTest;
-using CanonicalizeEntryPointIO = ast::transform::CanonicalizeEntryPointIO;
-using Unshadow = ast::transform::Unshadow;
-
-TEST_F(NumWorkgroupsFromUniformTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    EXPECT_FALSE(ShouldRun<NumWorkgroupsFromUniform>(src, data));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, ShouldRunHasNumWorkgroups) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    EXPECT_TRUE(ShouldRun<NumWorkgroupsFromUniform>(src, data));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, Error_MissingTransformData) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-}
-)";
-
-    auto* expect = "error: missing transform data for tint::hlsl::writer::NumWorkgroupsFromUniform";
-
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, Basic) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-  let groups_x = num_wgs.x;
-  let groups_y = num_wgs.y;
-  let groups_z = num_wgs.z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
-
-fn main_inner(num_wgs : vec3<u32>) {
-  let groups_x = num_wgs.x;
-  let groups_y = num_wgs.y;
-  let groups_z = num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  main_inner(tint_symbol_3.num_workgroups);
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, Basic_VarCopy) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-  var a = num_wgs;
-  let groups_x = a.x;
-  let groups_y = a.y;
-  let groups_z = a.z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
-
-fn main_inner(num_wgs : vec3<u32>) {
-  var a = num_wgs;
-  let groups_x = a.x;
-  let groups_y = a.y;
-  let groups_z = a.z;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  main_inner(tint_symbol_3.num_workgroups);
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, Basic_VarCopy_ViaPointerDerefDot) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-  var a = num_wgs;
-  let p = &a;
-  let groups_x = (*p).x;
-  let groups_y = (*p).y;
-  let groups_z = (*p).z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
-
-fn main_inner(num_wgs : vec3<u32>) {
-  var a = num_wgs;
-  let p = &(a);
-  let groups_x = (*(p)).x;
-  let groups_y = (*(p)).y;
-  let groups_z = (*(p)).z;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  main_inner(tint_symbol_3.num_workgroups);
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, Basic_VarCopy_ViaPointerDot) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-  var a = num_wgs;
-  let p = &a;
-  let groups_x = p.x;
-  let groups_y = p.y;
-  let groups_z = p.z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
-
-fn main_inner(num_wgs : vec3<u32>) {
-  var a = num_wgs;
-  let p = &(a);
-  let groups_x = p.x;
-  let groups_y = p.y;
-  let groups_z = p.z;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  main_inner(tint_symbol_3.num_workgroups);
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, StructOnlyMember) {
-    auto* src = R"(
-struct Builtins {
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-};
-
-@compute @workgroup_size(1)
-fn main(in : Builtins) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
-
-struct Builtins {
-  num_wgs : vec3<u32>,
-}
-
-fn main_inner(in : Builtins) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  main_inner(Builtins(tint_symbol_3.num_workgroups));
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, StructOnlyMember_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main(in : Builtins) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-struct Builtins {
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-};
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
-
-fn main_inner(in : Builtins) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main() {
-  main_inner(Builtins(tint_symbol_3.num_workgroups));
-}
-
-struct Builtins {
-  num_wgs : vec3<u32>,
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, StructMultipleMembers) {
-    auto* src = R"(
-struct Builtins {
-  @builtin(global_invocation_id) gid : vec3<u32>,
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-  @builtin(workgroup_id) wgid : vec3<u32>,
-};
-
-@compute @workgroup_size(1)
-fn main(in : Builtins) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
-
-struct Builtins {
-  gid : vec3<u32>,
-  num_wgs : vec3<u32>,
-  wgid : vec3<u32>,
-}
-
-struct tint_symbol_1 {
-  @builtin(global_invocation_id)
-  gid : vec3<u32>,
-  @builtin(workgroup_id)
-  wgid : vec3<u32>,
-}
-
-fn main_inner(in : Builtins) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main(tint_symbol : tint_symbol_1) {
-  main_inner(Builtins(tint_symbol.gid, tint_symbol_3.num_workgroups, tint_symbol.wgid));
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, StructMultipleMembers_OutOfOrder) {
-    auto* src = R"(
-@compute @workgroup_size(1)
-fn main(in : Builtins) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-struct Builtins {
-  @builtin(global_invocation_id) gid : vec3<u32>,
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-  @builtin(workgroup_id) wgid : vec3<u32>,
-};
-
-)";
-
-    auto* expect = R"(
-struct tint_symbol_2 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_3 : tint_symbol_2;
-
-struct tint_symbol_1 {
-  @builtin(global_invocation_id)
-  gid : vec3<u32>,
-  @builtin(workgroup_id)
-  wgid : vec3<u32>,
-}
-
-fn main_inner(in : Builtins) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main(tint_symbol : tint_symbol_1) {
-  main_inner(Builtins(tint_symbol.gid, tint_symbol_3.num_workgroups, tint_symbol.wgid));
-}
-
-struct Builtins {
-  gid : vec3<u32>,
-  num_wgs : vec3<u32>,
-  wgid : vec3<u32>,
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, MultipleEntryPoints) {
-    auto* src = R"(
-struct Builtins1 {
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-};
-
-struct Builtins2 {
-  @builtin(global_invocation_id) gid : vec3<u32>,
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-  @builtin(workgroup_id) wgid : vec3<u32>,
-};
-
-@compute @workgroup_size(1)
-fn main1(in : Builtins1) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main2(in : Builtins2) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main3(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-  let groups_x = num_wgs.x;
-  let groups_y = num_wgs.y;
-  let groups_z = num_wgs.z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_6 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(30) var<uniform> tint_symbol_7 : tint_symbol_6;
-
-struct Builtins1 {
-  num_wgs : vec3<u32>,
-}
-
-struct Builtins2 {
-  gid : vec3<u32>,
-  num_wgs : vec3<u32>,
-  wgid : vec3<u32>,
-}
-
-fn main1_inner(in : Builtins1) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main1() {
-  main1_inner(Builtins1(tint_symbol_7.num_workgroups));
-}
-
-struct tint_symbol_3 {
-  @builtin(global_invocation_id)
-  gid : vec3<u32>,
-  @builtin(workgroup_id)
-  wgid : vec3<u32>,
-}
-
-fn main2_inner(in : Builtins2) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main2(tint_symbol_2 : tint_symbol_3) {
-  main2_inner(Builtins2(tint_symbol_2.gid, tint_symbol_7.num_workgroups, tint_symbol_2.wgid));
-}
-
-fn main3_inner(num_wgs : vec3<u32>) {
-  let groups_x = num_wgs.x;
-  let groups_y = num_wgs.y;
-  let groups_z = num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main3() {
-  main3_inner(tint_symbol_7.num_workgroups);
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(NumWorkgroupsFromUniformTest, NoUsages) {
-    auto* src = R"(
-struct Builtins {
-  @builtin(global_invocation_id) gid : vec3<u32>,
-  @builtin(workgroup_id) wgid : vec3<u32>,
-};
-
-@compute @workgroup_size(1)
-fn main(in : Builtins) {
-}
-)";
-
-    auto* expect = R"(
-struct Builtins {
-  gid : vec3<u32>,
-  wgid : vec3<u32>,
-}
-
-struct tint_symbol_1 {
-  @builtin(global_invocation_id)
-  gid : vec3<u32>,
-  @builtin(workgroup_id)
-  wgid : vec3<u32>,
-}
-
-fn main_inner(in : Builtins) {
-}
-
-@compute @workgroup_size(1)
-fn main(tint_symbol : tint_symbol_1) {
-  main_inner(Builtins(tint_symbol.gid, tint_symbol.wgid));
-}
-)";
-
-    ast::transform::DataMap data;
-    data.Add<NumWorkgroupsFromUniform::Config>(BindingPoint{0, 30u});
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that group 0 binding 0 is used if no bound resource in the program and binding point is not
-// specified in NumWorkgroupsFromUniform::Config.
-TEST_F(NumWorkgroupsFromUniformTest, UnspecifiedBindingPoint_NoResourceBound) {
-    auto* src = R"(
-struct Builtins1 {
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-};
-
-struct Builtins2 {
-  @builtin(global_invocation_id) gid : vec3<u32>,
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-  @builtin(workgroup_id) wgid : vec3<u32>,
-};
-
-@compute @workgroup_size(1)
-fn main1(in : Builtins1) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main2(in : Builtins2) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main3(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-  let groups_x = num_wgs.x;
-  let groups_y = num_wgs.y;
-  let groups_z = num_wgs.z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_6 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(0) @binding(0) var<uniform> tint_symbol_7 : tint_symbol_6;
-
-struct Builtins1 {
-  num_wgs : vec3<u32>,
-}
-
-struct Builtins2 {
-  gid : vec3<u32>,
-  num_wgs : vec3<u32>,
-  wgid : vec3<u32>,
-}
-
-fn main1_inner(in : Builtins1) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main1() {
-  main1_inner(Builtins1(tint_symbol_7.num_workgroups));
-}
-
-struct tint_symbol_3 {
-  @builtin(global_invocation_id)
-  gid : vec3<u32>,
-  @builtin(workgroup_id)
-  wgid : vec3<u32>,
-}
-
-fn main2_inner(in : Builtins2) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main2(tint_symbol_2 : tint_symbol_3) {
-  main2_inner(Builtins2(tint_symbol_2.gid, tint_symbol_7.num_workgroups, tint_symbol_2.wgid));
-}
-
-fn main3_inner(num_wgs : vec3<u32>) {
-  let groups_x = num_wgs.x;
-  let groups_y = num_wgs.y;
-  let groups_z = num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main3() {
-  main3_inner(tint_symbol_7.num_workgroups);
-}
-)";
-
-    ast::transform::DataMap data;
-    // Make binding point unspecified.
-    data.Add<NumWorkgroupsFromUniform::Config>(std::nullopt);
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-// Test that binding 0 of the largest used group plus 1 is used if at least one resource is bound in
-// the program and binding point is not specified in NumWorkgroupsFromUniform::Config.
-TEST_F(NumWorkgroupsFromUniformTest, UnspecifiedBindingPoint_MultipleResourceBound) {
-    auto* src = R"(
-struct Builtins1 {
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-};
-
-struct Builtins2 {
-  @builtin(global_invocation_id) gid : vec3<u32>,
-  @builtin(num_workgroups) num_wgs : vec3<u32>,
-  @builtin(workgroup_id) wgid : vec3<u32>,
-};
-
-struct S0 {
-  @size(4)
-  m0 : u32,
-  m1 : array<u32>,
-};
-
-struct S1 {
-  @size(4)
-  m0 : u32,
-  m1 : array<u32, 6>,
-};
-
-@group(0) @binding(0) var g2 : texture_2d<f32>;
-@group(1) @binding(0) var g3 : texture_depth_2d;
-@group(1) @binding(1) var g4 : texture_storage_2d<rg32float, write>;
-@group(3) @binding(0) var g5 : texture_depth_cube_array;
-@group(4) @binding(0) var g6 : texture_external;
-
-@group(0) @binding(1) var<storage, read_write> g8 : S0;
-@group(1) @binding(3) var<storage, read> g9 : S0;
-@group(3) @binding(2) var<storage, read_write> g10 : S0;
-
-@compute @workgroup_size(1)
-fn main1(in : Builtins1) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-  g8.m0 = 1u;
-}
-
-@compute @workgroup_size(1)
-fn main2(in : Builtins2) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main3(@builtin(num_workgroups) num_wgs : vec3<u32>) {
-  let groups_x = num_wgs.x;
-  let groups_y = num_wgs.y;
-  let groups_z = num_wgs.z;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol_6 {
-  num_workgroups : vec3<u32>,
-}
-
-@group(5) @binding(0) var<uniform> tint_symbol_7 : tint_symbol_6;
-
-struct Builtins1 {
-  num_wgs : vec3<u32>,
-}
-
-struct Builtins2 {
-  gid : vec3<u32>,
-  num_wgs : vec3<u32>,
-  wgid : vec3<u32>,
-}
-
-struct S0 {
-  @size(4)
-  m0 : u32,
-  m1 : array<u32>,
-}
-
-struct S1 {
-  @size(4)
-  m0 : u32,
-  m1 : array<u32, 6>,
-}
-
-@group(0) @binding(0) var g2 : texture_2d<f32>;
-
-@group(1) @binding(0) var g3 : texture_depth_2d;
-
-@group(1) @binding(1) var g4 : texture_storage_2d<rg32float, write>;
-
-@group(3) @binding(0) var g5 : texture_depth_cube_array;
-
-@group(4) @binding(0) var g6 : texture_external;
-
-@group(0) @binding(1) var<storage, read_write> g8 : S0;
-
-@group(1) @binding(3) var<storage, read> g9 : S0;
-
-@group(3) @binding(2) var<storage, read_write> g10 : S0;
-
-fn main1_inner(in : Builtins1) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-  g8.m0 = 1u;
-}
-
-@compute @workgroup_size(1)
-fn main1() {
-  main1_inner(Builtins1(tint_symbol_7.num_workgroups));
-}
-
-struct tint_symbol_3 {
-  @builtin(global_invocation_id)
-  gid : vec3<u32>,
-  @builtin(workgroup_id)
-  wgid : vec3<u32>,
-}
-
-fn main2_inner(in : Builtins2) {
-  let groups_x = in.num_wgs.x;
-  let groups_y = in.num_wgs.y;
-  let groups_z = in.num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main2(tint_symbol_2 : tint_symbol_3) {
-  main2_inner(Builtins2(tint_symbol_2.gid, tint_symbol_7.num_workgroups, tint_symbol_2.wgid));
-}
-
-fn main3_inner(num_wgs : vec3<u32>) {
-  let groups_x = num_wgs.x;
-  let groups_y = num_wgs.y;
-  let groups_z = num_wgs.z;
-}
-
-@compute @workgroup_size(1)
-fn main3() {
-  main3_inner(tint_symbol_7.num_workgroups);
-}
-)";
-
-    ast::transform::DataMap data;
-    // Make binding point unspecified.
-    data.Add<NumWorkgroupsFromUniform::Config>(std::nullopt);
-    auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/pixel_local.cc b/src/tint/lang/hlsl/writer/ast_raise/pixel_local.cc
deleted file mode 100644
index 4f9f515..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/pixel_local.cc
+++ /dev/null
@@ -1,525 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "src/tint/lang/hlsl/writer/ast_raise/pixel_local.h"
-
-#include <string>
-#include <utility>
-
-#include "src/tint/lang/core/fluent_types.h"
-#include "src/tint/lang/wgsl/program/clone_context.h"
-#include "src/tint/lang/wgsl/resolver/resolve.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/module.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/struct.h"
-#include "src/tint/utils/containers/transform.h"
-#include "src/tint/utils/diagnostic/diagnostic.h"
-#include "src/tint/utils/rtti/switch.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::PixelLocal);
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::PixelLocal::RasterizerOrderedView);
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::PixelLocal::Config);
-
-using namespace tint::core::fluent_types;  // NOLINT
-
-namespace tint::hlsl::writer {
-
-/// PIMPL state for the transform
-struct PixelLocal::State {
-    /// The source program
-    const Program& src;
-    /// The semantic information of the source program
-    const sem::Info& sem;
-    /// The target program builder
-    ProgramBuilder b;
-    /// The clone context
-    program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
-    /// The transform config
-    const Config& cfg;
-
-    /// Constructor
-    /// @param program the source program
-    /// @param config the transform config
-    State(const Program& program, const Config& config)
-        : src(program), sem(program.Sem()), cfg(config) {}
-
-    /// Runs the transform
-    /// @returns the new program or SkipTransform if the transform is not required
-    ApplyResult Run() {
-        // If the pixel local extension isn't enabled, then there must be no use of pixel_local
-        // variables, and so there's nothing for this transform to do.
-        if (!sem.Module()->Extensions().Contains(
-                wgsl::Extension::kChromiumExperimentalPixelLocal)) {
-            return SkipTransform;
-        }
-
-        bool made_changes = false;
-
-        // Change all module scope `var<pixel_local>` variables to `var<private>`.
-        // We need to do this even if the variable is not referenced by the entry point as later
-        // stages do not understand the pixel_local address space.
-        for (auto* global : src.AST().GlobalVariables()) {
-            if (auto* var = global->As<ast::Var>()) {
-                if (sem.Get(var)->AddressSpace() == core::AddressSpace::kPixelLocal) {
-                    // Change the 'var<pixel_local>' to 'var<private>'
-                    ctx.Replace(var->declared_address_space, b.Expr(core::AddressSpace::kPrivate));
-                    made_changes = true;
-                }
-            }
-        }
-
-        // Find the single entry point
-        const sem::Function* entry_point = nullptr;
-        for (auto* fn : src.AST().Functions()) {
-            if (fn->IsEntryPoint()) {
-                if (entry_point != nullptr) {
-                    TINT_ICE() << "PixelLocal transform requires that the SingleEntryPoint "
-                                  "transform has already been run";
-                }
-                entry_point = sem.Get(fn);
-
-                // Look for a `var<pixel_local>` used by the entry point...
-                const tint::sem::GlobalVariable* pixel_local_variable = nullptr;
-                for (auto* global : entry_point->TransitivelyReferencedGlobals()) {
-                    if (global->AddressSpace() == core::AddressSpace::kPixelLocal) {
-                        pixel_local_variable = global;
-                        made_changes = true;
-                        break;
-                    }
-                }
-                if (pixel_local_variable == nullptr) {
-                    continue;
-                }
-
-                // Obtain struct of the pixel local.
-                auto* pixel_local_str =
-                    pixel_local_variable->Type()->UnwrapRef()->As<sem::Struct>();
-                if (auto res =
-                        TransformEntryPoint(entry_point, pixel_local_variable, pixel_local_str);
-                    res != Success) {
-                    b.Diagnostics().Add(res.Failure().reason);
-                    made_changes = true;
-                }
-
-                break;  // Only a single `var<pixel_local>` can be used by an entry point.
-            }
-        }
-
-        if (!made_changes) {
-            return SkipTransform;
-        }
-
-        ctx.Clone();
-        return resolver::Resolve(b);
-    }
-
-    /// Transforms the entry point @p entry_point to handle the direct or transitive usage of the
-    /// `var<pixel_local>` @p pixel_local_var.
-    /// @param entry_point the entry point
-    /// @param pixel_local_var the `var<pixel_local>`
-    /// @param pixel_local_str the struct type of the var
-    diag::Result<SuccessType> TransformEntryPoint(const sem::Function* entry_point,
-                                                  const sem::GlobalVariable* pixel_local_var,
-                                                  const sem::Struct* pixel_local_str) {
-        // Wrap the old entry point "fn" into a new entry point where functions to load and store
-        // ROV data are called.
-        auto* original_entry_point_fn = entry_point->Declaration();
-        auto entry_point_name = original_entry_point_fn->name->symbol.Name();
-
-        // Remove the @fragment attribute from the entry point
-        ctx.Remove(original_entry_point_fn->attributes,
-                   ast::GetAttribute<ast::StageAttribute>(original_entry_point_fn->attributes));
-        // Rename the entry point.
-        auto inner_function_name = b.Symbols().New(entry_point_name + "_inner");
-        ctx.Replace(original_entry_point_fn->name, b.Ident(inner_function_name));
-
-        // Create a new function that wraps the entry point.
-        // This function has all the existing entry point parameters and an additional
-        // parameter for the input pixel local structure.
-        auto new_entry_point_params = ctx.Clone(original_entry_point_fn->params);
-
-        // Remove any entry-point attributes from the inner function.
-        // This must come after `ctx.Clone(fn->params)` as we want these attributes on the outer
-        // function.
-        for (auto* param : original_entry_point_fn->params) {
-            for (auto* attr : param->attributes) {
-                if (attr->IsAnyOf<ast::BuiltinAttribute, ast::LocationAttribute,
-                                  ast::InterpolateAttribute, ast::InvariantAttribute>()) {
-                    ctx.Remove(param->attributes, attr);
-                }
-            }
-        }
-
-        // Declare the ROVs for the members of the pixel local variable and the functions to
-        // load data from and store data into the ROVs.
-        auto load_rov_function_name = b.Symbols().New("load_from_pixel_local_storage");
-        auto store_rov_function_name = b.Symbols().New("store_into_pixel_local_storage");
-        if (auto res = DeclareROVsAndLoadStoreFunctions(
-                load_rov_function_name, store_rov_function_name,
-                pixel_local_var->Declaration()->name->symbol.Name(), pixel_local_str);
-            res != Success) {
-            return res.Failure();
-        }
-
-        // Declare new entry point
-        Vector<const ast::Statement*, 5> new_entry_point_function_body;
-
-        // 1. let `hlsl_sv_position` be `@builtin(position)`
-        // Declare `@builtin(position)` in the input parameter of the new entry point if it is not
-        // declared in the original entry point.
-        auto sv_position_symbol = b.Symbols().New("hlsl_sv_position");
-        new_entry_point_function_body.Push(DeclareVariableWithBuiltinPosition(
-            new_entry_point_params, sv_position_symbol, entry_point));
-
-        // 2. Call `load_from_pixel_local_storage(hlsl_sv_position)`
-        new_entry_point_function_body.Push(
-            b.CallStmt(b.Call(load_rov_function_name, sv_position_symbol)));
-
-        // Declare the inner function
-        // Build the arguments to call the inner function
-        auto inner_function_call_args = tint::Transform(
-            original_entry_point_fn->params, [&](auto* p) { return b.Expr(ctx.Clone(p->name)); });
-
-        ast::Type new_entry_point_return_type;
-        if (original_entry_point_fn->return_type) {
-            // Create a structure to hold the combined flattened result of the entry point with
-            // `@location` attribute
-            auto new_entry_point_return_struct_name = b.Symbols().New(entry_point_name + "_res");
-            Vector<const ast::StructMember*, 8> members;
-            // arguments to the final `return` statement in the new entry point
-            Vector<const ast::Expression*, 8> new_entry_point_return_value_constructor_args;
-
-            auto add_member = [&](const core::type::Type* ty,
-                                  VectorRef<const ast::Attribute*> attrs) {
-                members.Push(b.Member("output_" + std::to_string(members.Length()),
-                                      CreateASTTypeFor(ctx, ty), std::move(attrs)));
-            };
-
-            Symbol inner_function_call_result = b.Symbols().New("result");
-            if (auto* str = entry_point->ReturnType()->As<sem::Struct>()) {
-                // The entry point returned a structure.
-                for (auto* member : str->Members()) {
-                    auto& member_attrs = member->Declaration()->attributes;
-                    add_member(member->Type(), ctx.Clone(member_attrs));
-                    new_entry_point_return_value_constructor_args.Push(
-                        b.MemberAccessor(inner_function_call_result, ctx.Clone(member->Name())));
-                    if (auto* location = ast::GetAttribute<ast::LocationAttribute>(member_attrs)) {
-                        // Remove the @location attribute from the member of the inner function's
-                        // output structure.
-                        // Note: This will break other entry points that share the same output
-                        // structure, however this transform assumes that the SingleEntryPoint
-                        // transform will have already been run.
-                        ctx.Remove(member_attrs, location);
-                    }
-                }
-            } else {
-                // The entry point returned a non-structure
-                add_member(entry_point->ReturnType(),
-                           ctx.Clone(original_entry_point_fn->return_type_attributes));
-                new_entry_point_return_value_constructor_args.Push(
-                    b.Expr(inner_function_call_result));
-
-                // Remove the @location from the inner function's return type attributes
-                ctx.Remove(original_entry_point_fn->return_type_attributes,
-                           ast::GetAttribute<ast::LocationAttribute>(
-                               original_entry_point_fn->return_type_attributes));
-            }
-
-            // 3. Call inner function and get the return value
-            new_entry_point_function_body.Push(
-                b.Decl(b.Let(inner_function_call_result,
-                             b.Call(inner_function_name, std::move(inner_function_call_args)))));
-
-            // Declare the output structure
-            b.Structure(new_entry_point_return_struct_name, std::move(members));
-
-            // 4. Call `store_into_pixel_local_storage(hlsl_sv_position)`
-            new_entry_point_function_body.Push(
-                b.CallStmt(b.Call(store_rov_function_name, sv_position_symbol)));
-
-            // 5. Return the output structure
-            new_entry_point_function_body.Push(
-                b.Return(b.Call(new_entry_point_return_struct_name,
-                                std::move(new_entry_point_return_value_constructor_args))));
-
-            new_entry_point_return_type = b.ty(new_entry_point_return_struct_name);
-        } else {
-            // 3. Call inner function without return value
-            new_entry_point_function_body.Push(
-                b.CallStmt(b.Call(inner_function_name, std::move(inner_function_call_args))));
-
-            // 4. Call `store_into_pixel_local_storage(hlsl_sv_position)`
-            new_entry_point_function_body.Push(
-                b.CallStmt(b.Call(store_rov_function_name, sv_position_symbol)));
-
-            new_entry_point_return_type = b.ty.void_();
-        }
-
-        // Declare the new entry point that calls the inner function
-        b.Func(entry_point_name, std::move(new_entry_point_params), new_entry_point_return_type,
-               new_entry_point_function_body, Vector{b.Stage(ast::PipelineStage::kFragment)});
-        return Success;
-    }
-
-    /// Add the declarations of all the ROVs as a special type of read-write storage texture that
-    /// represent the pixel local variable, the functions to load data from them and store data into
-    /// them.
-    /// @param load_rov_function_name the name of the funtion that loads the data from the ROVs
-    /// @param store_rov_function_name the name of the function that stores the data into the ROVs
-    /// @param pixel_local_variable_name the name of the pixel local variable
-    /// @param pixel_local_str the struct type of the pixel local variable
-    diag::Result<SuccessType> DeclareROVsAndLoadStoreFunctions(
-        const Symbol& load_rov_function_name,
-        const Symbol& store_rov_function_name,
-        const std::string& pixel_local_variable_name,
-        const sem::Struct* pixel_local_str) {
-        std::string_view load_store_input_name = "my_input";
-        Vector load_parameters{b.Param(load_store_input_name, b.ty.vec4<f32>())};
-        Vector store_parameters{b.Param(load_store_input_name, b.ty.vec4<f32>())};
-
-        // 1 declaration of `rov_texcoord` and at most 4 texture[Load|Store] calls (now the maximum
-        // size of PLS is 16)
-        Vector<const ast::Statement*, 5> load_body;
-        Vector<const ast::Statement*, 5> store_body;
-
-        // let rov_texcoord = vec2u(my_input.xy);
-        auto rov_texcoord = b.Symbols().New("rov_texcoord");
-        load_body.Push(b.Decl(
-            b.Let(rov_texcoord, b.Call("vec2u", b.MemberAccessor(load_store_input_name, "xy")))));
-        store_body.Push(b.Decl(
-            b.Let(rov_texcoord, b.Call("vec2u", b.MemberAccessor(load_store_input_name, "xy")))));
-
-        for (auto* member : pixel_local_str->Members()) {
-            // Declare the read-write storage texture with RasterizerOrderedView attribute.
-            auto member_format = Switch(
-                member->Type(),  //
-                [&](const core::type::U32*) { return core::TexelFormat::kR32Uint; },
-                [&](const core::type::I32*) { return core::TexelFormat::kR32Sint; },
-                [&](const core::type::F32*) { return core::TexelFormat::kR32Float; },
-                TINT_ICE_ON_NO_MATCH);
-            auto rov_format = ROVTexelFormat(member->Index());
-            if (DAWN_UNLIKELY(rov_format != Success)) {
-                return rov_format.Failure();
-            }
-            auto rov_type = b.ty.storage_texture(core::type::TextureDimension::k2d,
-                                                 rov_format.Get(), core::Access::kReadWrite);
-            auto rov_symbol_name = b.Symbols().New("pixel_local_" + member->Name().Name());
-            b.GlobalVar(rov_symbol_name, rov_type,
-                        tint::Vector{b.Binding(AInt(ROVRegisterIndex(member->Index()))),
-                                     b.Group(AInt(cfg.rov_group_index)), RasterizerOrderedView()});
-
-            // The function body of loading from PLS
-            // PLS_Private_Variable.member = textureLoad(pixel_local_member, rov_texcoord).x;
-            // Or
-            // PLS_Private_Variable.member =
-            //     bitcast(textureLoad(pixel_local_member, rov_texcoord).x);
-            auto pixel_local_var_member_access_in_load_call =
-                b.MemberAccessor(pixel_local_variable_name, ctx.Clone(member->Name()));
-            auto load_call = b.Call(wgsl::BuiltinFn::kTextureLoad, rov_symbol_name, rov_texcoord);
-            auto to_scalar_call = b.MemberAccessor(load_call, "x");
-            if (rov_format == member_format) {
-                load_body.Push(
-                    b.Assign(pixel_local_var_member_access_in_load_call, to_scalar_call));
-            } else {
-                auto member_ast_type = Switch(
-                    member->Type(),  //
-                    [&](const core::type::U32*) { return b.ty.u32(); },
-                    [&](const core::type::I32*) { return b.ty.i32(); },
-                    [&](const core::type::F32*) { return b.ty.f32(); },  //
-                    TINT_ICE_ON_NO_MATCH);
-
-                auto bitcast_to_member_type_call = b.Bitcast(member_ast_type, to_scalar_call);
-                load_body.Push(b.Assign(pixel_local_var_member_access_in_load_call,
-                                        bitcast_to_member_type_call));
-            }
-
-            // The function body of storing data into PLS
-            // textureStore(pixel_local_member, rov_texcoord, vec4u(PLS_Private_Variable.member));
-            // Or
-            // textureStore(
-            //     pixel_local_member, rov_texcoord, vec4u(bitcast(PLS_Private_Variable.member)));
-            std::string rov_pixel_type;
-            switch (rov_format.Get()) {
-                case core::TexelFormat::kR32Uint:
-                    rov_pixel_type = "vec4u";
-                    break;
-                case core::TexelFormat::kR32Sint:
-                    rov_pixel_type = "vec4i";
-                    break;
-                case core::TexelFormat::kR32Float:
-                    rov_pixel_type = "vec4f";
-                    break;
-                default:
-                    TINT_UNREACHABLE();
-            }
-            auto pixel_local_var_member_access_in_store_call =
-                b.MemberAccessor(pixel_local_variable_name, ctx.Clone(member->Name()));
-            const ast::CallExpression* to_vec4_call = nullptr;
-            if (rov_format == member_format) {
-                to_vec4_call = b.Call(rov_pixel_type, pixel_local_var_member_access_in_store_call);
-            } else {
-                ast::Type rov_pixel_ast_type;
-                switch (rov_format.Get()) {
-                    case core::TexelFormat::kR32Uint:
-                        rov_pixel_ast_type = b.ty.u32();
-                        break;
-                    case core::TexelFormat::kR32Sint:
-                        rov_pixel_ast_type = b.ty.i32();
-                        break;
-                    case core::TexelFormat::kR32Float:
-                        rov_pixel_ast_type = b.ty.f32();
-                        break;
-                    default:
-                        TINT_UNREACHABLE();
-                }
-                auto bitcast_to_rov_format_call =
-                    b.Bitcast(rov_pixel_ast_type, pixel_local_var_member_access_in_store_call);
-                to_vec4_call = b.Call(rov_pixel_type, bitcast_to_rov_format_call);
-            }
-            auto store_call =
-                b.Call(wgsl::BuiltinFn::kTextureStore, rov_symbol_name, rov_texcoord, to_vec4_call);
-            store_body.Push(b.CallStmt(store_call));
-        }
-
-        b.Func(load_rov_function_name, std::move(load_parameters), b.ty.void_(), load_body);
-        b.Func(store_rov_function_name, std::move(store_parameters), b.ty.void_(), store_body);
-        return Success;
-    }
-
-    /// Find and get `@builtin(position)` which is needed for loading and storing data with ROVs
-    /// @returns the statement object that initializes `new_entry_point_params` with
-    /// `@builtin(position)`.
-    /// @param new_entry_point_params the input parameters of the new entry point.
-    /// `@builtin(position)` may be added if it is not in `new_entry_point_params`.
-    /// @param variable_with_position_symbol the name of the variable that will be initialized with
-    /// `@builtin(position)`.
-    /// @param entry_point the semantic information of the entry point
-    const ast::VariableDeclStatement* DeclareVariableWithBuiltinPosition(
-        tint::Vector<const ast::Parameter*, 8>& new_entry_point_params,
-        const tint::Symbol& variable_with_position_symbol,
-        const sem::Function* entry_point) {
-        for (size_t i = 0; i < entry_point->Parameters().Length(); ++i) {
-            auto* parameter = entry_point->Parameters()[i];
-            // 1. `@builtin(position)` is declared as a member of a structure
-            if (auto* struct_type = parameter->Type()->As<sem::Struct>()) {
-                for (auto* member : struct_type->Members()) {
-                    if (member->Attributes().builtin == core::BuiltinValue::kPosition) {
-                        return b.Decl(b.Let(
-                            variable_with_position_symbol,
-                            b.MemberAccessor(new_entry_point_params[i], member->Name().Name())));
-                    }
-                }
-            }
-            // 2. `@builtin(position)` is declared as an individual input parameter
-            if (auto* attribute = ast::GetAttribute<ast::BuiltinAttribute>(
-                    parameter->Declaration()->attributes)) {
-                if (attribute->builtin == core::BuiltinValue::kPosition) {
-                    return b.Decl(
-                        b.Let(variable_with_position_symbol, b.Expr(new_entry_point_params[i])));
-                }
-            }
-        }
-
-        // 3. `@builtin(position)` is not declared in the input parameters and we should add one
-        auto* new_position = b.Param(b.Symbols().New("my_pos"), b.ty.vec4<f32>(),
-                                     Vector{b.Builtin(core::BuiltinValue::kPosition)});
-        new_entry_point_params.Push(new_position);
-        return b.Decl(b.Let(variable_with_position_symbol, b.Expr(new_position)));
-    }
-
-    /// @returns a new RasterizerOrderedView attribute
-    PixelLocal::RasterizerOrderedView* RasterizerOrderedView() {
-        return b.ASTNodes().Create<PixelLocal::RasterizerOrderedView>(b.ID(), b.AllocateNodeID());
-    }
-
-    /// @returns the register index for the pixel local field with the given index
-    /// @param field_index the pixel local field index
-    uint32_t ROVRegisterIndex(uint32_t field_index) {
-        auto idx = cfg.pls_member_to_rov_reg.Get(field_index);
-        if (DAWN_UNLIKELY(!idx)) {
-            b.Diagnostics().AddError(Source{})
-                << "PixelLocal::Config::attachments missing entry for field " << field_index;
-            return 0;
-        }
-        return *idx;
-    }
-
-    /// @returns the texel format for the pixel local field with the given index
-    /// @param field_index the pixel local field index
-    diag::Result<core::TexelFormat> ROVTexelFormat(uint32_t field_index) {
-        auto format = cfg.pls_member_to_rov_format.Get(field_index);
-        if (DAWN_UNLIKELY(!format)) {
-            diag::Diagnostic err;
-            err.severity = diag::Severity::Error;
-            err.message << "PixelLocal::Config::attachments missing entry for field "
-                        << field_index;
-            return diag::Failure{std::move(err)};
-        }
-        return *format;
-    }
-};
-
-PixelLocal::PixelLocal() = default;
-
-PixelLocal::~PixelLocal() = default;
-
-ast::transform::Transform::ApplyResult PixelLocal::Apply(const Program& src,
-                                                         const ast::transform::DataMap& inputs,
-                                                         ast::transform::DataMap&) const {
-    auto* cfg = inputs.Get<Config>();
-    if (!cfg) {
-        ProgramBuilder b;
-        b.Diagnostics().AddError(Source{}) << "missing transform data for " << TypeInfo().name;
-        return resolver::Resolve(b);
-    }
-
-    return State(src, *cfg).Run();
-}
-
-PixelLocal::Config::Config() = default;
-
-PixelLocal::Config::Config(const Config&) = default;
-
-PixelLocal::Config::~Config() = default;
-
-PixelLocal::RasterizerOrderedView::RasterizerOrderedView(GenerationID pid, ast::NodeID nid)
-    : Base(pid, nid, Empty) {}
-
-PixelLocal::RasterizerOrderedView::~RasterizerOrderedView() = default;
-
-std::string PixelLocal::RasterizerOrderedView::InternalName() const {
-    return "rov";
-}
-
-const PixelLocal::RasterizerOrderedView* PixelLocal::RasterizerOrderedView::Clone(
-    ast::CloneContext& ctx) const {
-    return ctx.dst->ASTNodes().Create<RasterizerOrderedView>(ctx.dst->ID(),
-                                                             ctx.dst->AllocateNodeID());
-}
-
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/pixel_local.h b/src/tint/lang/hlsl/writer/ast_raise/pixel_local.h
deleted file mode 100644
index 874b039..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/pixel_local.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_PIXEL_LOCAL_H_
-#define SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_PIXEL_LOCAL_H_
-
-#include <string>
-
-#include "src/tint/lang/core/texel_format.h"
-#include "src/tint/lang/wgsl/ast/internal_attribute.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::hlsl::writer {
-
-/// PixelLocal transforms module-scope `var<pixel_local>`s and fragment entry point functions that
-/// use them:
-/// * `var<pixel_local>` will be transformed to `var<private>`, a bunch of read-write storage
-///   textures with a special `RasterizerOrderedView` attribute, a function to read the data from
-///   thes read-write storage textures into the `var<private>` (ROV Reader), and a function to write
-///   the data from the `var<private>` into the read-write storage textures (ROV Writer).
-/// * The entry point function will be wrapped with another function ('outer') that calls the
-///  'inner' function.
-/// * The outer function will always have `@builtin(position)` in its input parameters.
-/// * The outer function will call the ROV reader before the original entry point function, and call
-///   the ROV writer after the original entry point function.
-/// @note PixelLocal requires that the SingleEntryPoint transform has already been run
-/// TODO(tint:2083): Optimize this in the future by inserting the load as late as possible and the
-/// store as early as possible.
-class PixelLocal final : public Castable<PixelLocal, ast::transform::Transform> {
-  public:
-    /// Transform configuration options
-    struct Config final : public Castable<Config, ast::transform::Data> {
-        /// Constructor
-        Config();
-
-        /// Copy Constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// Index of pixel_local structure member index to ROV register
-        Hashmap<uint32_t, uint32_t, 8> pls_member_to_rov_reg;
-
-        /// Index of pixel_local structure member index to ROV format
-        Hashmap<uint32_t, core::TexelFormat, 8> pls_member_to_rov_format;
-
-        /// Group index of all ROVs
-        uint32_t rov_group_index = 0;
-    };
-
-    /// RasterizerOrderedView is an InternalAttribute that's used to decorate a read-write storage
-    /// texture object.
-    class RasterizerOrderedView final
-        : public Castable<RasterizerOrderedView, ast::InternalAttribute> {
-      public:
-        /// Constructor
-        /// @param pid the identifier of the program that owns this node
-        /// @param nid the unique node identifier
-        RasterizerOrderedView(GenerationID pid, ast::NodeID nid);
-
-        /// Destructor
-        ~RasterizerOrderedView() override;
-
-        /// @return a short description of the internal attribute which will be
-        /// displayed as `@internal(<name>)`
-        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 RasterizerOrderedView* Clone(ast::CloneContext& ctx) const override;
-    };
-
-    /// Constructor
-    PixelLocal();
-
-    /// Destructor
-    ~PixelLocal() override;
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-
-  private:
-    struct State;
-};
-
-}  // namespace tint::hlsl::writer
-
-#endif  // SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_PIXEL_LOCAL_H_
diff --git a/src/tint/lang/hlsl/writer/ast_raise/pixel_local_test.cc b/src/tint/lang/hlsl/writer/ast_raise/pixel_local_test.cc
deleted file mode 100644
index f34138a..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/pixel_local_test.cc
+++ /dev/null
@@ -1,1312 +0,0 @@
-// Copyright 2023 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <utility>
-
-#include "src/tint/lang/hlsl/writer/ast_raise/pixel_local.h"
-
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-struct ROVBinding {
-    uint32_t field_index;
-    uint32_t register_index;
-    core::TexelFormat rov_format;
-};
-
-ast::transform::DataMap Bindings(std::initializer_list<ROVBinding> bindings,
-                                 uint32_t groupIndex = 0) {
-    PixelLocal::Config cfg;
-    cfg.rov_group_index = groupIndex;
-    for (auto& binding : bindings) {
-        cfg.pls_member_to_rov_reg.Add(binding.field_index, binding.register_index);
-        cfg.pls_member_to_rov_format.Add(binding.field_index, binding.rov_format);
-    }
-    ast::transform::DataMap data;
-    data.Add<PixelLocal::Config>(std::move(cfg));
-    return data;
-}
-
-using HLSLPixelLocalTest = ast::transform::TransformTest;
-
-TEST_F(HLSLPixelLocalTest, EmptyModule) {
-    auto* src = "";
-
-    EXPECT_FALSE(ShouldRun<PixelLocal>(src, Bindings({})));
-}
-
-TEST_F(HLSLPixelLocalTest, MissingBindings) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F() -> @location(0) vec4f {
-  P.a += 42;
-  return vec4f(1, 0, 0, 1);
-}
-)";
-
-    auto* expect = R"(error: PixelLocal::Config::attachments missing entry for field 0)";
-    ast::transform::DataMap data;
-    data.Add<PixelLocal::Config>();
-    auto got = Run<PixelLocal>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, UseInEntryPoint_NoPosition) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F() -> @location(0) vec4f {
-  P.a += 42;
-  return vec4f(1, 0, 0, 1);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-struct F_res {
-  @location(0)
-  output_0 : vec4<f32>,
-}
-
-@fragment
-fn F(@builtin(position) my_pos : vec4<f32>) -> F_res {
-  let hlsl_sv_position = my_pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  let result = F_inner();
-  store_into_pixel_local_storage(hlsl_sv_position);
-  return F_res(result);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner() -> vec4f {
-  P.a += 42;
-  return vec4f(1, 0, 0, 1);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, UseInEntryPoint_SeparatePosition) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@builtin(position) pos : vec4f) -> @location(0) vec4f {
-  P.a += 42;
-  return pos;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-struct F_res {
-  @location(0)
-  output_0 : vec4<f32>,
-}
-
-@fragment
-fn F(@builtin(position) pos : vec4f) -> F_res {
-  let hlsl_sv_position = pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  let result = F_inner(pos);
-  store_into_pixel_local_storage(hlsl_sv_position);
-  return F_res(result);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(pos : vec4f) -> vec4f {
-  P.a += 42;
-  return pos;
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, UseInEntryPoint_PositionInStruct) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0) input : vec4f,
-  @builtin(position) pos : vec4f,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> @location(0) vec4f {
-  P.a += 42;
-  return fragmentInput.input + fragmentInput.pos;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-struct F_res {
-  @location(0)
-  output_0 : vec4<f32>,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> F_res {
-  let hlsl_sv_position = fragmentInput.pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  let result = F_inner(fragmentInput);
-  store_into_pixel_local_storage(hlsl_sv_position);
-  return F_res(result);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0)
-  input : vec4f,
-  @builtin(position)
-  pos : vec4f,
-}
-
-fn F_inner(fragmentInput : FragmentInput) -> vec4f {
-  P.a += 42;
-  return (fragmentInput.input + fragmentInput.pos);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, UseInEntryPoint_NonZeroROVGroupIndex) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0) input : vec4f,
-  @builtin(position) pos : vec4f,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> @location(0) vec4f {
-  P.a += 42;
-  return fragmentInput.input + fragmentInput.pos;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(3) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-struct F_res {
-  @location(0)
-  output_0 : vec4<f32>,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> F_res {
-  let hlsl_sv_position = fragmentInput.pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  let result = F_inner(fragmentInput);
-  store_into_pixel_local_storage(hlsl_sv_position);
-  return F_res(result);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0)
-  input : vec4f,
-  @builtin(position)
-  pos : vec4f,
-}
-
-fn F_inner(fragmentInput : FragmentInput) -> vec4f {
-  P.a += 42;
-  return (fragmentInput.input + fragmentInput.pos);
-}
-)";
-
-    constexpr uint32_t kROVGroupIndex = 3u;
-    auto got =
-        Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}, kROVGroupIndex));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, UseInEntryPoint_Multiple_PixelLocal_Members) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-  b : i32,
-  c : f32,
-  d : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0) input : vec4f,
-  @builtin(position) pos : vec4f,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> @location(0) vec4f {
-  P.a += 42;
-  P.b -= 21;
-  P.c += 12.5f;
-  P.d -= 5;
-  return fragmentInput.input + fragmentInput.pos;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-@binding(2) @group(0) @internal(rov) var pixel_local_b : texture_storage_2d<r32sint, read_write>;
-
-@binding(3) @group(0) @internal(rov) var pixel_local_c : texture_storage_2d<r32float, read_write>;
-
-@binding(4) @group(0) @internal(rov) var pixel_local_d : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-  P.b = textureLoad(pixel_local_b, rov_texcoord).x;
-  P.c = textureLoad(pixel_local_c, rov_texcoord).x;
-  P.d = textureLoad(pixel_local_d, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-  textureStore(pixel_local_b, rov_texcoord, vec4i(P.b));
-  textureStore(pixel_local_c, rov_texcoord, vec4f(P.c));
-  textureStore(pixel_local_d, rov_texcoord, vec4u(P.d));
-}
-
-struct F_res {
-  @location(0)
-  output_0 : vec4<f32>,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> F_res {
-  let hlsl_sv_position = fragmentInput.pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  let result = F_inner(fragmentInput);
-  store_into_pixel_local_storage(hlsl_sv_position);
-  return F_res(result);
-}
-
-struct PixelLocal {
-  a : u32,
-  b : i32,
-  c : f32,
-  d : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0)
-  input : vec4f,
-  @builtin(position)
-  pos : vec4f,
-}
-
-fn F_inner(fragmentInput : FragmentInput) -> vec4f {
-  P.a += 42;
-  P.b -= 21;
-  P.c += 12.5f;
-  P.d -= 5;
-  return (fragmentInput.input + fragmentInput.pos);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint},
-                                              {1, 2, core::TexelFormat::kR32Sint},
-                                              {2, 3, core::TexelFormat::kR32Float},
-                                              {3, 4, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, UseInEntryPoint_Multiple_PixelLocal_Members_And_Fragment_Output) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-  b : i32,
-  c : f32,
-  d : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0) input : vec4f,
-  @builtin(position) pos : vec4f,
-}
-
-struct FragmentOutput {
-  @location(0) color0 : vec4f,
-  @location(1) color1 : vec4f,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> FragmentOutput {
-  P.a += 42;
-  P.b -= 21;
-  P.c += 12.5f;
-  P.d -= 5;
-  return FragmentOutput(fragmentInput.input, fragmentInput.pos);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-@binding(2) @group(0) @internal(rov) var pixel_local_b : texture_storage_2d<r32sint, read_write>;
-
-@binding(3) @group(0) @internal(rov) var pixel_local_c : texture_storage_2d<r32float, read_write>;
-
-@binding(4) @group(0) @internal(rov) var pixel_local_d : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-  P.b = textureLoad(pixel_local_b, rov_texcoord).x;
-  P.c = textureLoad(pixel_local_c, rov_texcoord).x;
-  P.d = textureLoad(pixel_local_d, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-  textureStore(pixel_local_b, rov_texcoord, vec4i(P.b));
-  textureStore(pixel_local_c, rov_texcoord, vec4f(P.c));
-  textureStore(pixel_local_d, rov_texcoord, vec4u(P.d));
-}
-
-struct F_res {
-  @location(0)
-  output_0 : vec4<f32>,
-  @location(1)
-  output_1 : vec4<f32>,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> F_res {
-  let hlsl_sv_position = fragmentInput.pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  let result = F_inner(fragmentInput);
-  store_into_pixel_local_storage(hlsl_sv_position);
-  return F_res(result.color0, result.color1);
-}
-
-struct PixelLocal {
-  a : u32,
-  b : i32,
-  c : f32,
-  d : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0)
-  input : vec4f,
-  @builtin(position)
-  pos : vec4f,
-}
-
-struct FragmentOutput {
-  color0 : vec4f,
-  color1 : vec4f,
-}
-
-fn F_inner(fragmentInput : FragmentInput) -> FragmentOutput {
-  P.a += 42;
-  P.b -= 21;
-  P.c += 12.5f;
-  P.d -= 5;
-  return FragmentOutput(fragmentInput.input, fragmentInput.pos);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint},
-                                              {1, 2, core::TexelFormat::kR32Sint},
-                                              {2, 3, core::TexelFormat::kR32Float},
-                                              {3, 4, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, ROV_Format_Different_From_Pixel_Local_Member_Format) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-  b : i32,
-  c : f32,
-  d : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0) input : vec4f,
-  @builtin(position) pos : vec4f,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> @location(0) vec4f {
-  P.a += 42;
-  P.b -= 21;
-  P.c += 12.5f;
-  P.d -= 5;
-  return fragmentInput.input + fragmentInput.pos;
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32sint, read_write>;
-
-@binding(2) @group(0) @internal(rov) var pixel_local_b : texture_storage_2d<r32float, read_write>;
-
-@binding(3) @group(0) @internal(rov) var pixel_local_c : texture_storage_2d<r32uint, read_write>;
-
-@binding(4) @group(0) @internal(rov) var pixel_local_d : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = bitcast<u32>(textureLoad(pixel_local_a, rov_texcoord).x);
-  P.b = bitcast<i32>(textureLoad(pixel_local_b, rov_texcoord).x);
-  P.c = bitcast<f32>(textureLoad(pixel_local_c, rov_texcoord).x);
-  P.d = textureLoad(pixel_local_d, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4i(bitcast<i32>(P.a)));
-  textureStore(pixel_local_b, rov_texcoord, vec4f(bitcast<f32>(P.b)));
-  textureStore(pixel_local_c, rov_texcoord, vec4u(bitcast<u32>(P.c)));
-  textureStore(pixel_local_d, rov_texcoord, vec4u(P.d));
-}
-
-struct F_res {
-  @location(0)
-  output_0 : vec4<f32>,
-}
-
-@fragment
-fn F(fragmentInput : FragmentInput) -> F_res {
-  let hlsl_sv_position = fragmentInput.pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  let result = F_inner(fragmentInput);
-  store_into_pixel_local_storage(hlsl_sv_position);
-  return F_res(result);
-}
-
-struct PixelLocal {
-  a : u32,
-  b : i32,
-  c : f32,
-  d : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct FragmentInput {
-  @location(0)
-  input : vec4f,
-  @builtin(position)
-  pos : vec4f,
-}
-
-fn F_inner(fragmentInput : FragmentInput) -> vec4f {
-  P.a += 42;
-  P.b -= 21;
-  P.c += 12.5f;
-  P.d -= 5;
-  return (fragmentInput.input + fragmentInput.pos);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Sint},
-                                              {1, 2, core::TexelFormat::kR32Float},
-                                              {2, 3, core::TexelFormat::kR32Uint},
-                                              {3, 4, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, UseInCallee) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-fn X() {
-  P.a += 42;
-}
-
-@fragment
-fn F() {
-  X();
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(@builtin(position) my_pos : vec4<f32>) {
-  let hlsl_sv_position = my_pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  F_inner();
-  store_into_pixel_local_storage(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn X() {
-  P.a += 42;
-}
-
-fn F_inner() {
-  X();
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, ROVLoadFunctionNameUsed) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  P.a += u32(my_input.x);
-}
-
-@fragment
-fn F() {
-  load_from_pixel_local_storage(vec4f(1.0, 0.0, 0.0, 1.0));
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage_1(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(@builtin(position) my_pos : vec4<f32>) {
-  let hlsl_sv_position = my_pos;
-  load_from_pixel_local_storage_1(hlsl_sv_position);
-  F_inner();
-  store_into_pixel_local_storage(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  P.a += u32(my_input.x);
-}
-
-fn F_inner() {
-  load_from_pixel_local_storage(vec4f(1.0, 0.0, 0.0, 1.0));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, ROVStoreFunctionNameUsed) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  P.a += u32(my_input.x);
-}
-
-@fragment
-fn F() {
-  store_into_pixel_local_storage(vec4f(1.0, 0.0, 0.0, 1.0));
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage_1(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(@builtin(position) my_pos : vec4<f32>) {
-  let hlsl_sv_position = my_pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  F_inner();
-  store_into_pixel_local_storage_1(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  P.a += u32(my_input.x);
-}
-
-fn F_inner() {
-  store_into_pixel_local_storage(vec4f(1.0, 0.0, 0.0, 1.0));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, NewBuiltinPositionVariableNamesUsed) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@location(0) my_pos : vec4f) {
-  let hlsl_sv_position = my_pos * 2;
-  P.a += u32(hlsl_sv_position.y);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(@location(0) my_pos : vec4f, @builtin(position) my_pos_1 : vec4<f32>) {
-  let hlsl_sv_position_1 = my_pos_1;
-  load_from_pixel_local_storage(hlsl_sv_position_1);
-  F_inner(my_pos);
-  store_into_pixel_local_storage(hlsl_sv_position_1);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(my_pos : vec4f) {
-  let hlsl_sv_position = (my_pos * 2);
-  P.a += u32(hlsl_sv_position.y);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, WithInvariantBuiltinInputParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@invariant @builtin(position) pos : vec4f) {
-  P.a += u32(pos.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(@invariant @builtin(position) pos : vec4f) {
-  let hlsl_sv_position = pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  F_inner(pos);
-  store_into_pixel_local_storage(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(pos : vec4f) {
-  P.a += u32(pos.x);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, WithInvariantBuiltinInputStructParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct In {
-  @invariant @builtin(position) pos : vec4f,
-}
-
-@fragment
-fn F(in : In) {
-  P.a += u32(in.pos.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(in : In) {
-  let hlsl_sv_position = in.pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  F_inner(in);
-  store_into_pixel_local_storage(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct In {
-  @invariant @builtin(position)
-  pos : vec4f,
-}
-
-fn F_inner(in : In) {
-  P.a += u32(in.pos.x);
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, WithLocationInputParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@location(0) a : vec4f, @interpolate(flat) @location(1) b : vec4f) {
-  P.a += u32(a.x) + u32(b.y);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(@location(0) a : vec4f, @interpolate(flat) @location(1) b : vec4f, @builtin(position) my_pos : vec4<f32>) {
-  let hlsl_sv_position = my_pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  F_inner(a, b);
-  store_into_pixel_local_storage(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(a : vec4f, b : vec4f) {
-  P.a += (u32(a.x) + u32(b.y));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, WithLocationInputStructParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct In {
-  @location(0) a : vec4f,
-  @interpolate(flat) @location(1) b : vec4f,
-}
-
-@fragment
-fn F(in : In) {
-  P.a += u32(in.a.x) + u32(in.b.y);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(in : In, @builtin(position) my_pos : vec4<f32>) {
-  let hlsl_sv_position = my_pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  F_inner(in);
-  store_into_pixel_local_storage(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct In {
-  @location(0)
-  a : vec4f,
-  @interpolate(flat) @location(1)
-  b : vec4f,
-}
-
-fn F_inner(in : In) {
-  P.a += (u32(in.a.x) + u32(in.b.y));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, WithBuiltinAndLocationInputParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-@fragment
-fn F(@builtin(position) pos : vec4f, @location(0) uv : vec4f) {
-  P.a += u32(pos.x) + u32(uv.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(@builtin(position) pos : vec4f, @location(0) uv : vec4f) {
-  let hlsl_sv_position = pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  F_inner(pos, uv);
-  store_into_pixel_local_storage(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-fn F_inner(pos : vec4f, uv : vec4f) {
-  P.a += (u32(pos.x) + u32(uv.x));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(HLSLPixelLocalTest, WithBuiltinAndLocationInputStructParameter) {
-    auto* src = R"(
-enable chromium_experimental_pixel_local;
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<pixel_local> P : PixelLocal;
-
-struct In {
-  @builtin(position) pos : vec4f,
-  @location(0) uv : vec4f,
-}
-
-@fragment
-fn F(in : In) {
-  P.a += u32(in.pos.x) + u32(in.uv.x);
-}
-)";
-
-    auto* expect =
-        R"(
-enable chromium_experimental_pixel_local;
-
-@binding(1) @group(0) @internal(rov) var pixel_local_a : texture_storage_2d<r32uint, read_write>;
-
-fn load_from_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  P.a = textureLoad(pixel_local_a, rov_texcoord).x;
-}
-
-fn store_into_pixel_local_storage(my_input : vec4<f32>) {
-  let rov_texcoord = vec2u(my_input.xy);
-  textureStore(pixel_local_a, rov_texcoord, vec4u(P.a));
-}
-
-@fragment
-fn F(in : In) {
-  let hlsl_sv_position = in.pos;
-  load_from_pixel_local_storage(hlsl_sv_position);
-  F_inner(in);
-  store_into_pixel_local_storage(hlsl_sv_position);
-}
-
-struct PixelLocal {
-  a : u32,
-}
-
-var<private> P : PixelLocal;
-
-struct In {
-  @builtin(position)
-  pos : vec4f,
-  @location(0)
-  uv : vec4f,
-}
-
-fn F_inner(in : In) {
-  P.a += (u32(in.pos.x) + u32(in.uv.x));
-}
-)";
-
-    auto got = Run<PixelLocal>(src, Bindings({{0, 1, core::TexelFormat::kR32Uint}}));
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.cc b/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.cc
deleted file mode 100644
index 605136e..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.cc
+++ /dev/null
@@ -1,214 +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/hlsl/writer/ast_raise/truncate_interstage_variables.h"
-
-#include <memory>
-#include <string>
-#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/call.h"
-#include "src/tint/lang/wgsl/sem/function.h"
-#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
-#include "src/tint/lang/wgsl/sem/statement.h"
-#include "src/tint/lang/wgsl/sem/variable.h"
-#include "src/tint/utils/text/unicode.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::TruncateInterstageVariables);
-TINT_INSTANTIATE_TYPEINFO(tint::hlsl::writer::TruncateInterstageVariables::Config);
-
-namespace tint::hlsl::writer {
-
-namespace {
-
-struct TruncatedStructAndConverter {
-    /// The symbol of the truncated structure.
-    Symbol truncated_struct;
-    /// The symbol of the helper function that takes the original structure as a single argument and
-    /// returns the truncated structure type.
-    Symbol truncate_fn;
-};
-
-}  // anonymous namespace
-
-TruncateInterstageVariables::TruncateInterstageVariables() = default;
-TruncateInterstageVariables::~TruncateInterstageVariables() = default;
-
-ast::transform::Transform::ApplyResult TruncateInterstageVariables::Apply(
-    const Program& src,
-    const ast::transform::DataMap& config,
-    ast::transform::DataMap&) const {
-    ProgramBuilder b;
-    program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true};
-
-    const auto* data = config.Get<Config>();
-    if (data == nullptr) {
-        b.Diagnostics().AddError(Source{})
-            << "missing transform data for "
-            << tint::TypeInfo::Of<TruncateInterstageVariables>().name;
-        return resolver::Resolve(b);
-    }
-
-    auto& sem = ctx.src->Sem();
-
-    bool should_run = false;
-
-    Hashmap<const sem::Function*, Symbol, 4u> entry_point_functions_to_truncate_functions;
-    Hashmap<const sem::Struct*, TruncatedStructAndConverter, 4u>
-        old_shader_io_structs_to_new_struct_and_truncate_functions;
-
-    for (auto* func_ast : ctx.src->AST().Functions()) {
-        if (!func_ast->IsEntryPoint()) {
-            continue;
-        }
-
-        if (func_ast->PipelineStage() != ast::PipelineStage::kVertex) {
-            // Currently only vertex stage could have interstage output variables that need
-            // truncated.
-            continue;
-        }
-
-        auto* func_sem = sem.Get(func_ast);
-        auto* str = func_sem->ReturnType()->As<sem::Struct>();
-
-        // This transform is run after CanonicalizeEntryPointIO transform,
-        // So it is guaranteed that entry point inputs are already grouped in a struct.
-        if (DAWN_UNLIKELY(!str)) {
-            TINT_ICE() << "Entrypoint function return type is non-struct.\n"
-                       << "TruncateInterstageVariables transform needs to run after "
-                          "CanonicalizeEntryPointIO transform.";
-        }
-
-        // A prepass to check if any interstage variable locations in the entry point needs
-        // truncating. If not we don't really need to handle this entry point.
-        Hashset<const sem::StructMember*, 16u> omit_members;
-
-        for (auto* member : str->Members()) {
-            if (auto location = member->Attributes().location) {
-                const size_t kMaxLocation = data->interstage_locations.size() - 1u;
-                if (location.value() > kMaxLocation) {
-                    b.Diagnostics().AddError(Source{})
-                        << "The location (" << location.value() << ") of " << member->Name().Name()
-                        << " in " << str->Name().Name() << " exceeds the maximum value ("
-                        << kMaxLocation << ").";
-                    return resolver::Resolve(b);
-                }
-                if (!data->interstage_locations.test(location.value())) {
-                    omit_members.Add(member);
-                }
-            }
-        }
-
-        if (omit_members.IsEmpty()) {
-            continue;
-        }
-
-        // Now we are sure the transform needs to be run.
-        should_run = true;
-
-        // Get or create a new truncated struct/truncate function for the interstage inputs &
-        // outputs.
-        auto entry = old_shader_io_structs_to_new_struct_and_truncate_functions.GetOrAdd(str, [&] {
-            auto new_struct_sym = b.Symbols().New();
-
-            Vector<const ast::StructMember*, 20> truncated_members;
-            Vector<const ast::Expression*, 20> initializer_exprs;
-
-            for (auto* member : str->Members()) {
-                if (omit_members.Contains(member)) {
-                    continue;
-                }
-
-                truncated_members.Push(ctx.Clone(member->Declaration()));
-                initializer_exprs.Push(b.MemberAccessor("io", ctx.Clone(member->Name())));
-            }
-
-            // Create the new shader io struct.
-            b.Structure(new_struct_sym, std::move(truncated_members));
-
-            // Create the mapping function to truncate the shader io.
-            auto mapping_fn_sym = b.Symbols().New("truncate_shader_output");
-            b.Func(mapping_fn_sym, Vector{b.Param("io", ctx.Clone(func_ast->return_type))},
-                   b.ty(new_struct_sym),
-                   Vector{
-                       b.Return(b.Call(new_struct_sym, std::move(initializer_exprs))),
-                   });
-            return TruncatedStructAndConverter{new_struct_sym, mapping_fn_sym};
-        });
-
-        ctx.Replace(func_ast->return_type.expr, b.Expr(entry.truncated_struct));
-
-        entry_point_functions_to_truncate_functions.Add(func_sem, entry.truncate_fn);
-    }
-
-    if (!should_run) {
-        return SkipTransform;
-    }
-
-    // Replace return statements with new truncated shader IO struct
-    ctx.ReplaceAll(
-        [&](const ast::ReturnStatement* return_statement) -> const ast::ReturnStatement* {
-            auto* return_sem = sem.Get(return_statement);
-            if (auto mapping_fn_sym =
-                    entry_point_functions_to_truncate_functions.Get(return_sem->Function())) {
-                return b.Return(return_statement->source,
-                                b.Call(*mapping_fn_sym, ctx.Clone(return_statement->value)));
-            }
-            return nullptr;
-        });
-
-    // Remove IO attributes from old shader IO struct which is not used as entry point output
-    // anymore.
-    for (auto& it : old_shader_io_structs_to_new_struct_and_truncate_functions) {
-        const ast::Struct* struct_ty = it.key->Declaration();
-        for (auto* member : struct_ty->members) {
-            for (auto* attr : member->attributes) {
-                if (attr->IsAnyOf<ast::BuiltinAttribute, ast::LocationAttribute,
-                                  ast::InterpolateAttribute, ast::InvariantAttribute>()) {
-                    ctx.Remove(member->attributes, attr);
-                }
-            }
-        }
-    }
-
-    ctx.Clone();
-    return resolver::Resolve(b);
-}
-
-TruncateInterstageVariables::Config::Config() = default;
-
-TruncateInterstageVariables::Config::Config(const Config&) = default;
-
-TruncateInterstageVariables::Config::~Config() = default;
-
-TruncateInterstageVariables::Config& TruncateInterstageVariables::Config::operator=(const Config&) =
-    default;
-
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h b/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h
deleted file mode 100644
index 17b1f10..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h
+++ /dev/null
@@ -1,144 +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_HLSL_WRITER_AST_RAISE_TRUNCATE_INTERSTAGE_VARIABLES_H_
-#define SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_TRUNCATE_INTERSTAGE_VARIABLES_H_
-
-#include <bitset>
-
-#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/wgsl/ast/transform/transform.h"
-
-namespace tint::hlsl::writer {
-
-/// TruncateInterstageVariables is a transform that truncate interstage variables.
-/// It must be run after CanonicalizeEntryPointIO which guarantees all interstage variables of
-/// a given entry point are grouped into one shader IO struct.
-/// It replaces `original shader IO struct` with a `new wrapper struct` containing builtin IOs
-/// and user-defined IO whose locations are marked in the interstage_locations bitset from the
-/// config. The return statements of `original shader IO struct` are wrapped by a mapping function
-/// that initializes the members of `new wrapper struct` with values from `original shader IO
-/// struct`. IO attributes of members in `original shader IO struct` are removed, other attributes
-/// still preserve.
-///
-/// For example:
-///
-/// ```
-///  struct ShaderIO {
-///    @builtin(position) @invariant pos: vec4<f32>,
-///    @location(1) f_1: f32,
-///    @location(3) @align(16) f_3: f32,
-///    @location(5) @interpolate(flat) @align(16) @size(16) f_5: u32,
-///  }
-///  @vertex
-///  fn f() -> ShaderIO {
-///    var io: ShaderIO;
-///    io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-///    io.f_1 = 1.0;
-///    io.f_3 = io.f_1 + 3.0;
-///    io.f_5 = 1u;
-///    return io;
-///  }
-/// ```
-///
-/// With config.interstage_locations[3] and [5] set to true, is transformed to:
-///
-/// ```
-///  struct tint_symbol {
-///    @builtin(position) @invariant
-///    pos : vec4<f32>,
-///    @location(3) @align(16)
-///    f_3 : f32,
-///    @location(5) @interpolate(flat) @align(16) @size(16)
-///    f_5 : u32,
-///  }
-///
-///  fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
-///    return tint_symbol(io.pos, io.f_3, io.f_5);
-///  }
-///
-///  struct ShaderIO {
-///    pos : vec4<f32>,
-///    f_1 : f32,
-///    @align(16)
-///    f_3 : f32,
-///    @align(16) @size(16)
-///    f_5 : u32,
-///  }
-///
-///  @vertex
-///  fn f() -> tint_symbol {
-///    var io : ShaderIO;
-///    io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-///    io.f_1 = 1.0;
-///    io.f_3 = (io.f_1 + 3.0);
-///    io.f_5 = 1u;
-///    return truncate_shader_output(io);
-///  }
-/// ```
-///
-class TruncateInterstageVariables final
-    : public Castable<TruncateInterstageVariables, ast::transform::Transform> {
-  public:
-    /// Configuration options for the transform
-    struct Config final : public Castable<Config, ast::transform::Data> {
-        /// Constructor
-        Config();
-
-        /// Copy constructor
-        Config(const Config&);
-
-        /// Destructor
-        ~Config() override;
-
-        /// Assignment operator
-        /// @returns this Config
-        Config& operator=(const Config&);
-
-        /// Indicate which interstage io locations are actually used by the later stage.
-        /// There can be at most 30 user defined interstage variables with locations.
-        std::bitset<30> interstage_locations;
-
-        /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-        TINT_REFLECT(Config, interstage_locations);
-    };
-
-    /// Constructor using a the configuration provided in the input Data
-    TruncateInterstageVariables();
-
-    /// Destructor
-    ~TruncateInterstageVariables() override;
-
-    /// @copydoc ast::transform::Transform::Apply
-    ApplyResult Apply(const Program& program,
-                      const ast::transform::DataMap& inputs,
-                      ast::transform::DataMap& outputs) const override;
-};
-
-}  // namespace tint::hlsl::writer
-
-#endif  // SRC_TINT_LANG_HLSL_WRITER_AST_RAISE_TRUNCATE_INTERSTAGE_VARIABLES_H_
diff --git a/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables_test.cc b/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables_test.cc
deleted file mode 100644
index f5cd0c1..0000000
--- a/src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables_test.cc
+++ /dev/null
@@ -1,641 +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/hlsl/writer/ast_raise/truncate_interstage_variables.h"
-#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
-
-#include "gmock/gmock.h"
-#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-using ::testing::ContainerEq;
-
-using TruncateInterstageVariablesTest = ast::transform::TransformTest;
-
-TEST(TintCheckAllFieldsReflected, HlslWriterAstRaiseTruncateInterstageVariablesTest) {
-    TINT_ASSERT_ALL_FIELDS_REFLECTED(TruncateInterstageVariables::Config);
-}
-
-TEST_F(TruncateInterstageVariablesTest, ShouldRunVertex) {
-    auto* src = R"(
-struct ShaderIO {
-  @builtin(position) pos: vec4<f32>,
-  @location(0) f_0: f32,
-  @location(2) f_2: f32,
-}
-@vertex
-fn f() -> ShaderIO {
-  var io: ShaderIO;
-  io.f_0 = 1.0;
-  io.f_2 = io.f_2 + 3.0;
-  return io;
-}
-)";
-
-    {
-        auto* expect =
-            "error: missing transform data for tint::hlsl::writer::TruncateInterstageVariables";
-        auto got = Run<TruncateInterstageVariables>(src);
-        EXPECT_EQ(expect, str(got));
-    }
-
-    {
-        // Empty interstage_locations: truncate all interstage variables, should run
-        TruncateInterstageVariables::Config cfg;
-        ast::transform::DataMap data;
-        data.Add<TruncateInterstageVariables::Config>(cfg);
-        EXPECT_TRUE(ShouldRun<TruncateInterstageVariables>(src, data));
-    }
-
-    {
-        // All existing interstage_locations are marked: should not run
-        TruncateInterstageVariables::Config cfg;
-        cfg.interstage_locations[0] = true;
-        cfg.interstage_locations[2] = true;
-        ast::transform::DataMap data;
-        data.Add<TruncateInterstageVariables::Config>(cfg);
-        EXPECT_FALSE(ShouldRun<TruncateInterstageVariables>(src, data));
-    }
-
-    {
-        // Partial interstage_locations are marked: should run
-        TruncateInterstageVariables::Config cfg;
-        cfg.interstage_locations[2] = true;
-        ast::transform::DataMap data;
-        data.Add<TruncateInterstageVariables::Config>(cfg);
-        EXPECT_TRUE(ShouldRun<TruncateInterstageVariables>(src, data));
-    }
-}
-
-TEST_F(TruncateInterstageVariablesTest, ShouldRunFragment) {
-    auto* src = R"(
-struct ShaderIO {
-  @location(0) f_0: f32,
-  @location(2) f_2: f32,
-}
-@fragment
-fn f(io: ShaderIO) -> @location(1) vec4<f32> {
-  return vec4<f32>(io.f_0, io.f_2, 0.0, 1.0);
-}
-)";
-
-    TruncateInterstageVariables::Config cfg;
-    cfg.interstage_locations[2] = true;
-
-    ast::transform::DataMap data;
-    data.Add<TruncateInterstageVariables::Config>(cfg);
-
-    EXPECT_FALSE(ShouldRun<TruncateInterstageVariables>(src, data));
-}
-
-// Test that this transform should run after canoicalize entry point io, where shader io is already
-// grouped into a struct.
-TEST_F(TruncateInterstageVariablesTest, ShouldRunAfterCanonicalizeEntryPointIO) {
-    auto* src = R"(
-@vertex
-fn f() -> @builtin(position) vec4<f32> {
-  return vec4<f32>(1.0, 1.0, 1.0, 1.0);
-}
-)";
-
-    TruncateInterstageVariables::Config cfg;
-    cfg.interstage_locations[0] = true;
-    ast::transform::DataMap data;
-    data.Add<TruncateInterstageVariables::Config>(cfg);
-
-    auto got = Run<ast::transform::CanonicalizeEntryPointIO>(src, data);
-
-    // Inevitably entry point can write only one variable if not using struct
-    // So the truncate won't run.
-    EXPECT_FALSE(ShouldRun<TruncateInterstageVariables>(str(got), data));
-}
-
-TEST_F(TruncateInterstageVariablesTest, BasicVertexTrimLocationInMid) {
-    auto* src = R"(
-struct ShaderIO {
-  @builtin(position) pos: vec4<f32>,
-  @location(1) f_1: f32,
-  @location(3) f_3: f32,
-}
-@vertex
-fn f() -> ShaderIO {
-  var io: ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = io.f_1 + 3.0;
-  return io;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @builtin(position)
-  pos : vec4<f32>,
-  @location(1)
-  f_1 : f32,
-}
-
-fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
-  return tint_symbol(io.pos, io.f_1);
-}
-
-struct ShaderIO {
-  pos : vec4<f32>,
-  f_1 : f32,
-  f_3 : f32,
-}
-
-@vertex
-fn f() -> tint_symbol {
-  var io : ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = (io.f_1 + 3.0);
-  return truncate_shader_output(io);
-}
-)";
-
-    TruncateInterstageVariables::Config cfg;
-    // fragment has input at @location(1)
-    cfg.interstage_locations[1] = true;
-
-    ast::transform::DataMap data;
-    data.Add<TruncateInterstageVariables::Config>(cfg);
-
-    auto got = Run<TruncateInterstageVariables>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(TruncateInterstageVariablesTest, BasicVertexTrimLocationAtEnd) {
-    auto* src = R"(
-struct ShaderIO {
-  @builtin(position) pos: vec4<f32>,
-  @location(1) f_1: f32,
-  @location(3) f_3: f32,
-}
-@vertex
-fn f() -> ShaderIO {
-  var io: ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = io.f_1 + 3.0;
-  return io;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @builtin(position)
-  pos : vec4<f32>,
-  @location(3)
-  f_3 : f32,
-}
-
-fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
-  return tint_symbol(io.pos, io.f_3);
-}
-
-struct ShaderIO {
-  pos : vec4<f32>,
-  f_1 : f32,
-  f_3 : f32,
-}
-
-@vertex
-fn f() -> tint_symbol {
-  var io : ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = (io.f_1 + 3.0);
-  return truncate_shader_output(io);
-}
-)";
-
-    TruncateInterstageVariables::Config cfg;
-    // fragment has input at @location(3)
-    cfg.interstage_locations[3] = true;
-
-    ast::transform::DataMap data;
-    data.Add<TruncateInterstageVariables::Config>(cfg);
-
-    auto got = Run<TruncateInterstageVariables>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(TruncateInterstageVariablesTest, TruncateAllLocations) {
-    auto* src = R"(
-struct ShaderIO {
-  @builtin(position) pos: vec4<f32>,
-  @location(1) f_1: f32,
-  @location(3) f_3: f32,
-}
-@vertex
-fn f() -> ShaderIO {
-  var io: ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = io.f_1 + 3.0;
-  return io;
-}
-)";
-
-    {
-        auto* expect = R"(
-struct tint_symbol {
-  @builtin(position)
-  pos : vec4<f32>,
-}
-
-fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
-  return tint_symbol(io.pos);
-}
-
-struct ShaderIO {
-  pos : vec4<f32>,
-  f_1 : f32,
-  f_3 : f32,
-}
-
-@vertex
-fn f() -> tint_symbol {
-  var io : ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = (io.f_1 + 3.0);
-  return truncate_shader_output(io);
-}
-)";
-
-        TruncateInterstageVariables::Config cfg;
-        ast::transform::DataMap data;
-        data.Add<TruncateInterstageVariables::Config>(cfg);
-
-        auto got = Run<TruncateInterstageVariables>(src, data);
-
-        EXPECT_EQ(expect, str(got));
-    }
-}
-
-// Test that the transform only removes IO attributes and preserve other attributes in the old
-// Shader IO struct.
-TEST_F(TruncateInterstageVariablesTest, RemoveIOAttributes) {
-    auto* src = R"(
-struct ShaderIO {
-  @builtin(position) @invariant pos: vec4<f32>,
-  @location(1) f_1: f32,
-  @location(3) @align(16) f_3: f32,
-  @location(5) @interpolate(flat) @align(16) @size(16) f_5: u32,
-}
-@vertex
-fn f() -> ShaderIO {
-  var io: ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = io.f_1 + 3.0;
-  io.f_5 = 1u;
-  return io;
-}
-)";
-
-    {
-        auto* expect = R"(
-struct tint_symbol {
-  @builtin(position) @invariant
-  pos : vec4<f32>,
-  @location(3) @align(16)
-  f_3 : f32,
-  @location(5) @interpolate(flat) @align(16) @size(16)
-  f_5 : u32,
-}
-
-fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
-  return tint_symbol(io.pos, io.f_3, io.f_5);
-}
-
-struct ShaderIO {
-  pos : vec4<f32>,
-  f_1 : f32,
-  @align(16)
-  f_3 : f32,
-  @align(16) @size(16)
-  f_5 : u32,
-}
-
-@vertex
-fn f() -> tint_symbol {
-  var io : ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = (io.f_1 + 3.0);
-  io.f_5 = 1u;
-  return truncate_shader_output(io);
-}
-)";
-
-        TruncateInterstageVariables::Config cfg;
-        // Missing @location[1] intentionally to make sure the transform run.
-        cfg.interstage_locations[3] = true;
-        cfg.interstage_locations[5] = true;
-
-        ast::transform::DataMap data;
-        data.Add<TruncateInterstageVariables::Config>(cfg);
-
-        auto got = Run<TruncateInterstageVariables>(src, data);
-
-        EXPECT_EQ(expect, str(got));
-    }
-}
-
-TEST_F(TruncateInterstageVariablesTest, MultipleEntryPointsSharingStruct) {
-    auto* src = R"(
-struct ShaderIO {
-  @builtin(position) pos: vec4<f32>,
-  @location(1) f_1: f32,
-  @location(3) f_3: f32,
-  @location(5) f_5: f32,
-}
-
-@vertex
-fn f1() -> ShaderIO {
-  var io: ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = 1.0;
-  return io;
-}
-
-@vertex
-fn f2() -> ShaderIO {
-  var io: ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_5 = 2.0;
-  return io;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @builtin(position)
-  pos : vec4<f32>,
-  @location(3)
-  f_3 : f32,
-}
-
-fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
-  return tint_symbol(io.pos, io.f_3);
-}
-
-struct ShaderIO {
-  pos : vec4<f32>,
-  f_1 : f32,
-  f_3 : f32,
-  f_5 : f32,
-}
-
-@vertex
-fn f1() -> tint_symbol {
-  var io : ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = 1.0;
-  return truncate_shader_output(io);
-}
-
-@vertex
-fn f2() -> tint_symbol {
-  var io : ShaderIO;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_5 = 2.0;
-  return truncate_shader_output(io);
-}
-)";
-
-    TruncateInterstageVariables::Config cfg;
-    // fragment has input at @location(3)
-    cfg.interstage_locations[3] = true;
-
-    ast::transform::DataMap data;
-    data.Add<TruncateInterstageVariables::Config>(cfg);
-
-    auto got = Run<TruncateInterstageVariables>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(TruncateInterstageVariablesTest, MultipleEntryPoints) {
-    auto* src = R"(
-struct ShaderIO1 {
-  @builtin(position) pos: vec4<f32>,
-  @location(1) f_1: f32,
-  @location(3) f_3: f32,
-  @location(5) f_5: f32,
-}
-
-@vertex
-fn f1() -> ShaderIO1 {
-  var io: ShaderIO1;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = 1.0;
-  return io;
-}
-
-struct ShaderIO2 {
-  @builtin(position) pos: vec4<f32>,
-  @location(2) f_2: f32,
-}
-
-@vertex
-fn f2() -> ShaderIO2 {
-  var io: ShaderIO2;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_2 = 2.0;
-  return io;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @builtin(position)
-  pos : vec4<f32>,
-  @location(3)
-  f_3 : f32,
-}
-
-fn truncate_shader_output(io : ShaderIO1) -> tint_symbol {
-  return tint_symbol(io.pos, io.f_3);
-}
-
-struct tint_symbol_1 {
-  @builtin(position)
-  pos : vec4<f32>,
-}
-
-fn truncate_shader_output_1(io : ShaderIO2) -> tint_symbol_1 {
-  return tint_symbol_1(io.pos);
-}
-
-struct ShaderIO1 {
-  pos : vec4<f32>,
-  f_1 : f32,
-  f_3 : f32,
-  f_5 : f32,
-}
-
-@vertex
-fn f1() -> tint_symbol {
-  var io : ShaderIO1;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = 1.0;
-  return truncate_shader_output(io);
-}
-
-struct ShaderIO2 {
-  pos : vec4<f32>,
-  f_2 : f32,
-}
-
-@vertex
-fn f2() -> tint_symbol_1 {
-  var io : ShaderIO2;
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_2 = 2.0;
-  return truncate_shader_output_1(io);
-}
-)";
-
-    TruncateInterstageVariables::Config cfg;
-    // fragment has input at @location(3)
-    cfg.interstage_locations[3] = true;
-
-    ast::transform::DataMap data;
-    data.Add<TruncateInterstageVariables::Config>(cfg);
-
-    auto got = Run<TruncateInterstageVariables>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(TruncateInterstageVariablesTest, MultipleReturnStatements) {
-    auto* src = R"(
-struct ShaderIO {
-  @builtin(position) pos: vec4<f32>,
-  @location(1) f_1: f32,
-  @location(3) f_3: f32,
-}
-@vertex
-fn f(@builtin(vertex_index) vid: u32) -> ShaderIO {
-  var io: ShaderIO;
-  if (vid > 10u) {
-    io.f_1 = 2.0;
-    return io;
-  }
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = io.f_1 + 3.0;
-  return io;
-}
-)";
-
-    auto* expect = R"(
-struct tint_symbol {
-  @builtin(position)
-  pos : vec4<f32>,
-  @location(3)
-  f_3 : f32,
-}
-
-fn truncate_shader_output(io : ShaderIO) -> tint_symbol {
-  return tint_symbol(io.pos, io.f_3);
-}
-
-struct ShaderIO {
-  pos : vec4<f32>,
-  f_1 : f32,
-  f_3 : f32,
-}
-
-@vertex
-fn f(@builtin(vertex_index) vid : u32) -> tint_symbol {
-  var io : ShaderIO;
-  if ((vid > 10u)) {
-    io.f_1 = 2.0;
-    return truncate_shader_output(io);
-  }
-  io.pos = vec4<f32>(1.0, 1.0, 1.0, 1.0);
-  io.f_1 = 1.0;
-  io.f_3 = (io.f_1 + 3.0);
-  return truncate_shader_output(io);
-}
-)";
-
-    TruncateInterstageVariables::Config cfg;
-    // fragment has input at @location(3)
-    cfg.interstage_locations[3] = true;
-
-    ast::transform::DataMap data;
-    data.Add<TruncateInterstageVariables::Config>(cfg);
-
-    auto got = Run<TruncateInterstageVariables>(src, data);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(TruncateInterstageVariablesTest, LocationOutOfRange) {
-    auto* src = R"(
-struct ShaderIO {
-  @builtin(position) pos: vec4<f32>,
-  @location(0) f_0: f32,
-  @location(30) f_2: f32,
-}
-@vertex
-fn f() -> ShaderIO {
-  var io: ShaderIO;
-  io.f_0 = 1.0;
-  io.f_2 = io.f_2 + 3.0;
-  return io;
-}
-)";
-
-    // Return error when location >= 30 (maximum supported number of inter-stage shader variables)
-    auto* expect = "error: The location (30) of f_2 in ShaderIO exceeds the maximum value (29).";
-
-    TruncateInterstageVariables::Config cfg;
-    ast::transform::DataMap data;
-    data.Add<TruncateInterstageVariables::Config>(cfg);
-
-    auto got = Run<TruncateInterstageVariables>(src, data);
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/writer.cc b/src/tint/lang/hlsl/writer/writer.cc
index 1d09c2a..2d09c13 100644
--- a/src/tint/lang/hlsl/writer/writer.cc
+++ b/src/tint/lang/hlsl/writer/writer.cc
@@ -34,10 +34,9 @@
 #include "src/tint/lang/core/ir/module.h"
 #include "src/tint/lang/core/ir/var.h"
 #include "src/tint/lang/core/type/input_attachment.h"
-#include "src/tint/lang/hlsl/writer/ast_printer/ast_printer.h"
+#include "src/tint/lang/core/type/pointer.h"
 #include "src/tint/lang/hlsl/writer/printer/printer.h"
 #include "src/tint/lang/hlsl/writer/raise/raise.h"
-#include "src/tint/lang/wgsl/ast/pipeline_stage.h"
 
 namespace tint::hlsl::writer {
 
@@ -102,38 +101,4 @@
     return Print(ir, options);
 }
 
-Result<Output> Generate(const Program& program, const Options& options) {
-    if (!program.IsValid()) {
-        return Failure{program.Diagnostics().Str()};
-    }
-
-    // Sanitize the program.
-    auto sanitized_result = Sanitize(program, options);
-    if (!sanitized_result.program.IsValid()) {
-        return Failure{sanitized_result.program.Diagnostics().Str()};
-    }
-
-    // Generate the HLSL code.
-    auto impl = std::make_unique<ASTPrinter>(sanitized_result.program);
-    if (!impl->Generate()) {
-        return Failure{impl->Diagnostics().Str()};
-    }
-
-    Output output;
-    output.hlsl = impl->Result();
-
-    // Collect the list of entry points in the sanitized program.
-    for (auto* func : sanitized_result.program.AST().Functions()) {
-        if (func->IsEntryPoint()) {
-            auto name = func->name->symbol.Name();
-            output.entry_points.push_back({name, func->PipelineStage()});
-        }
-    }
-
-    output.used_array_length_from_uniform_indices =
-        std::move(sanitized_result.used_array_length_from_uniform_indices);
-
-    return output;
-}
-
 }  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/writer.h b/src/tint/lang/hlsl/writer/writer.h
index bb1a7f2..a637e76 100644
--- a/src/tint/lang/hlsl/writer/writer.h
+++ b/src/tint/lang/hlsl/writer/writer.h
@@ -33,9 +33,6 @@
 #include "src/tint/utils/result.h"
 
 // Forward declarations
-namespace tint {
-class Program;
-}  // namespace tint
 namespace tint::core::ir {
 class Module;
 }  // namespace tint::core::ir
@@ -55,13 +52,6 @@
 /// @returns the resulting HLSL and supplementary information, or failure
 Result<Output> Generate(core::ir::Module& ir, const Options& options);
 
-/// Generate HLSL for a program, according to a set of configuration options.
-/// The result will contain the HLSL and supplementary information, or failure.
-/// @param program the program to translate to HLSL
-/// @param options the configuration options to use when generating HLSL
-/// @returns the resulting HLSL and supplementary information, or failure
-Result<Output> Generate(const Program& program, const Options& options);
-
 }  // namespace tint::hlsl::writer
 
 #endif  // SRC_TINT_LANG_HLSL_WRITER_WRITER_H_
diff --git a/src/tint/lang/hlsl/writer/writer_ast_fuzz.cc b/src/tint/lang/hlsl/writer/writer_ast_fuzz.cc
deleted file mode 100644
index f011ab4..0000000
--- a/src/tint/lang/hlsl/writer/writer_ast_fuzz.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2024 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// GEN_BUILD:CONDITION(tint_build_wgsl_reader)
-
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/cmd/fuzz/wgsl/fuzz.h"
-#include "src/tint/lang/hlsl/validate/validate.h"
-#include "src/tint/lang/hlsl/writer/writer.h"
-#include "src/tint/lang/wgsl/ast/module.h"
-#include "src/tint/lang/wgsl/ast/transform/renamer.h"
-#include "src/tint/utils/command/command.h"
-#include "src/tint/utils/text/string.h"
-
-namespace tint::hlsl::writer {
-namespace {
-
-bool CanRun(const Program& program) {
-    if (program.AST().HasOverrides()) {
-        return false;
-    }
-
-    // The PixelLocal transform assumes only a single entry point, so check for multiple entry
-    // points if chromium_experimental_pixel_local is enabled.
-    uint32_t num_entry_points = 0;
-    for (auto* fn : program.AST().Functions()) {
-        if (fn->PipelineStage() != ast::PipelineStage::kNone) {
-            num_entry_points++;
-        }
-    }
-    for (auto* enable : program.AST().Enables()) {
-        if (enable->HasExtension(tint::wgsl::Extension::kChromiumExperimentalSubgroupMatrix)) {
-            return false;
-        }
-        if (enable->HasExtension(tint::wgsl::Extension::kChromiumExperimentalPixelLocal)) {
-            if (num_entry_points > 1) {
-                return false;
-            }
-            break;
-        }
-    }
-
-    return true;
-}
-
-void ASTFuzzer(const tint::Program& program,
-               const fuzz::wgsl::Context& context,
-               const Options& options) {
-    if (!CanRun(program)) {
-        return;
-    }
-
-    // Currently disabled, as DXC can error on HLSL emitted by Tint. For example: post optimization
-    // infinite loops will fail to compile, but these are beyond Tint's analysis capabilities.
-    constexpr bool must_validate = false;
-
-    const char* dxc_path = validate::kDxcDLLName;
-    if (!context.options.dxc.empty()) {
-        dxc_path = context.options.dxc.c_str();
-    }
-    auto dxc = tint::Command::LookPath(dxc_path);
-
-    Result<tint::hlsl::writer::Output> res;
-    if (dxc.Found()) {
-        // If validating with DXC, run renamer transform first to avoid DXC validation failures.
-        ast::transform::DataMap inputs, outputs;
-        inputs.Add<ast::transform::Renamer::Config>(ast::transform::Renamer::Target::kHlslKeywords);
-        if (auto renamer_res = tint::ast::transform::Renamer{}.Apply(program, inputs, outputs)) {
-            if (!renamer_res->IsValid()) {
-                TINT_ICE() << renamer_res->Diagnostics();
-            }
-            res = tint::hlsl::writer::Generate(*renamer_res, options);
-        }
-    } else {
-        res = tint::hlsl::writer::Generate(program, options);
-    }
-
-    if (res != Success) {
-        return;
-    }
-
-    if (context.options.dump) {
-        std::cout << "Dumping generated HLSL:\n" << res->hlsl << "\n";
-    }
-
-    if (dxc.Found()) {
-        uint32_t hlsl_shader_model = 60;
-        bool require_16bit_types = false;
-        auto enable_list = program.AST().Enables();
-        for (auto* enable : enable_list) {
-            if (enable->HasExtension(tint::wgsl::Extension::kF16)) {
-                hlsl_shader_model = 62;
-                require_16bit_types = true;
-                break;
-            }
-        }
-
-        auto validate_res = validate::ValidateUsingDXC(dxc.Path(), res->hlsl, res->entry_points,
-                                                       require_16bit_types, hlsl_shader_model);
-
-        if (must_validate && validate_res.failed) {
-            size_t line_num = 1;
-            std::stringstream err;
-            err << "DXC was expected to succeed, but failed:\n\n";
-            for (auto line : Split(res->hlsl, "\n")) {
-                err << line_num++ << ": " << line << "\n";
-            }
-            err << "\n\n" << validate_res.output;
-            TINT_ICE() << err.str();
-        }
-
-    } else if (must_validate) {
-        TINT_ICE() << "cannot validate with DXC as it was not found at: " << dxc_path;
-    }
-}
-
-}  // namespace
-}  // namespace tint::hlsl::writer
-
-TINT_WGSL_PROGRAM_FUZZER(tint::hlsl::writer::ASTFuzzer);