Import Tint changes from Dawn

Contains a manual fix for CMakeList.txt changes.

Changes: - b6903295a826f5378b0445c36da7859373645e45 tint/resolver: Support error cases with const-eval builti... by Ben Clayton <bclayton@google.com>
  - 6fcc4f3a54eff1f2b134d0ec4fa5c69db83adb80 tint: const eval of reverseBits by Antonio Maiorano <amaiorano@google.com>
  - a4314fabb471f491688b7275327cd31481d3bad7 Minor build/include fixes for google3 roll. by Loko Kung <lokokung@google.com>
  - 16b4cf87d0890a0e9bd3bd41df828b8a029a4a26 Template cmake for generated files. by dan sinclair <dsinclair@chromium.org>
  - 6590cc4fd929c4ed6b3043c1c6bf769de20c2a7b Add missing benchmark test. by dan sinclair <dsinclair@chromium.org>
  - 8fa6c25b7868d2445413e20b3deaf117a02446ad tint: Add Initialize() / Shutdown() public APIs by Ben Clayton <bclayton@google.com>
  - 02f04d914d21b30fdda5d69dea08076043be36de tint/transform: Polyfill bit-shift with RHS modulo by Ben Clayton <bclayton@google.com>
  - 91e27f25f984e2156eb535421f1650c8a459fb70 Add const-eval for `floor`. by dan sinclair <dsinclair@chromium.org>
  - 11cecc1e4ffb1c9604ab3d7407dc625918636354 Revert "Add CMake build option to generate intrinsic file... by dan sinclair <dsinclair@chromium.org>
  - abd6a7d9d4a40c2b109cca4cc1ccd420409333bc Add const-eval for `ceil` by dan sinclair <dsinclair@chromium.org>
  - 465df13196c674ebe60eaf30efc6220c88e30457 Revert "This CL updates the cmake files to use go run dir... by Dan Sinclair <dsinclair@chromium.org>
  - 9cbc7e1e54b5b6385c4529cdc30f9398514aadf1 Implement const-eval for `acos` by dan sinclair <dsinclair@chromium.org>
  - b3518d8b4dd9be70af0bf166bac7f5921c2725e0 Implement const eval of `abs` by dan sinclair <dsinclair@chromium.org>
  - 5ae03c266a590cd119dd16e2eaaaef60b943df12 Revert "Scaffolding for generation of intrinsics files." by Dan Sinclair <dsinclair@chromium.org>
  - 11f0c52bfb4bb22293f7ba867dbd33fd4f89d139 tint: const eval of extractBits by Antonio Maiorano <amaiorano@google.com>
  - 58eca19f33a7068c2571a430746922faa03a0871 Add const-eval for `all`. by dan sinclair <dsinclair@chromium.org>
  - aa4b64f4c8d1456a70e2f66f305457d255b952d3 tint/utils: Optimise HashmapBase::Scan() by Ben Clayton <bclayton@google.com>
  - c6b381495db0bdabb8fd9eb1f33e0097099bbd7e tint/transform: Refactor transforms by Ben Clayton <bclayton@google.com>
  - 336f3536e5c6b7a0b63dd7c92c58770d5a0812b5 tint: const eval of insertBits by Antonio Maiorano <amaiorano@google.com>
  - e372511e1ba20f24fde0e1999fd318daeea13581 tint/utils: Rework Hashmap / Hashset by Ben Clayton <bclayton@google.com>
  - 9535f7220913acd92d59c76009cb31b9410b1e21 tint/validator: Hint 'var' instead of 'const' by Ben Clayton <bclayton@google.com>
  - fd9c3fe4bcedb2f1aed628c27fe6d38a768f2a89 tint/uniforimty: Remove short-circuit special-case by James Price <jrprice@google.com>
  - c81f9dce07e5ea41e8aa1f65bc619b6ed669403b tint: Implement const-eval of quantizeToF16 by Ben Clayton <bclayton@google.com>
  - 749abeaafb609a9d1b0be758489cc4d4bc137ba8 Revert "Add GN build option to build using generated file... by Dan Sinclair <dsinclair@chromium.org>
  - a950d4539ed6e6846616804b2b986f79460bc484 This CL updates the cmake files to use go run directly. by dan sinclair <dsinclair@chromium.org>
  - bdd5bbe0c5d3b0f0a1800d7ec0cf814376ec3dde tint/transform/utils: HoistToDeclBefore polish by Ben Clayton <bclayton@google.com>
  - 2d63e321fd6c0342ba405204df0f978c3b3865be tint: Add callback overloads to CloneContext::Insert* by Ben Clayton <bclayton@google.com>
  - b5c213b9951681beca81de2b7fda3c448df850f0 tint/resolver: Fix failures with no error by Ben Clayton <bclayton@google.com>
  - 6ab5d3c1519e3b8d76598a20a5fdc3bb84d35fd2 Tint/transform: make AddBlockAttribute always do wrapping... by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - 2bea9055f429c3b7708b2fa33f4539c1ce98c6ad tint: Implement runtime quantizeToF16() by Ben Clayton <bclayton@google.com>
  - 35da05462438b316889e88fe0f81a8e58500ee7a tint/cmake: Fix test source paths by James Price <jrprice@google.com>
  - ed0649dec4f1324320d4f678819eaa48e57a5aed [IR] Fix setting of if merge target by dan sinclair <dsinclair@chromium.org>
  - 9261261da87ee8bfc9564c5addec3d59cf4308e0 [IR] Add ability to dump IR graph to dot by dan sinclair <dsinclair@chromium.org>
  - 1c755b68e6ff93fa734c43e5b45e4cd528c92c11 [IR] Add support for `for` statements by dan sinclair <dsinclair@chromium.org>
  - 0dce067e286e3d72b929af1f06e2cc332aee2dc5 [IR] Add support for `while` statements. by dan sinclair <dsinclair@chromium.org>
  - 7cd8db11558e427820d646219a69787b99e86431 Add GN build option to build using generated files. by dan sinclair <dsinclair@chromium.org>
  - 50c29f6d38b3d471bc4036faae11020530538d86 [IR] Add support for `fallthrough`. by dan sinclair <dsinclair@chromium.org>
  - c22b8b9dc8c058fef45c72a3c2cdd70d90fc911d Add CMake build option to generate intrinsic files. by dan sinclair <dsinclair@chromium.org>
  - c02feff258e22a54de14f8afae851d0c3665e6f5 tint/cmd: Add `--rename-all` flag by Ben Clayton <bclayton@google.com>
  - d336733c8338ae5bf3f4eadf9bcf8c286cd20fba [IR] Add switch control flow node. by dan sinclair <dsinclair@chromium.org>
  - b9ed75cf2fe5ab8d759da17a9cfe0befb286a1a1 [IR] Track inbound branches to flow nodes. by dan sinclair <dsinclair@chromium.org>
  - 6c5db2afa69756f8f07efd942c88ad5b173fc738 Scaffolding for generation of intrinsics files. by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: b6903295a826f5378b0445c36da7859373645e45
Change-Id: Ie32c6158b8dab82f8097785131a34c44b9892986
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/108921
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: dan sinclair <dsinclair@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Ben Clayton <bclayton@chromium.org>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9d72db1..51252d2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,9 @@
 option_if_not_defined(TINT_BUILD_MSL_WRITER "Build the MSL output writer" ON)
 option_if_not_defined(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ON)
 option_if_not_defined(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
+
+option_if_not_defined(TINT_BUILD_IR "Build the IR" ON)
+
 option_if_not_defined(TINT_BUILD_FUZZERS "Build fuzzers" OFF)
 option_if_not_defined(TINT_BUILD_SPIRV_TOOLS_FUZZER "Build SPIRV-Tools fuzzer" OFF)
 option_if_not_defined(TINT_BUILD_AST_FUZZER "Build AST fuzzer" OFF)
@@ -110,6 +113,7 @@
 message(STATUS "Tint build MSL writer: ${TINT_BUILD_MSL_WRITER}")
 message(STATUS "Tint build SPIR-V writer: ${TINT_BUILD_SPV_WRITER}")
 message(STATUS "Tint build WGSL writer: ${TINT_BUILD_WGSL_WRITER}")
+message(STATUS "Tint build IR: ${TINT_BUILD_IR}")
 message(STATUS "Tint build fuzzers: ${TINT_BUILD_FUZZERS}")
 message(STATUS "Tint build SPIRV-Tools fuzzer: ${TINT_BUILD_SPIRV_TOOLS_FUZZER}")
 message(STATUS "Tint build AST fuzzer: ${TINT_BUILD_AST_FUZZER}")
@@ -274,6 +278,7 @@
   target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_MSL_WRITER=$<BOOL:${TINT_BUILD_MSL_WRITER}>)
   target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_SPV_WRITER=$<BOOL:${TINT_BUILD_SPV_WRITER}>)
   target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_WGSL_WRITER=$<BOOL:${TINT_BUILD_WGSL_WRITER}>)
+  target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_IR=$<BOOL:${TINT_BUILD_IR}>)
 
   if (COMPILER_IS_LIKE_GNU)
     target_compile_options(${TARGET} PRIVATE
diff --git a/include/tint/override_id.h b/include/tint/override_id.h
index 957673d..05e56a7 100644
--- a/include/tint/override_id.h
+++ b/include/tint/override_id.h
@@ -16,6 +16,7 @@
 #define SRC_TINT_OVERRIDE_ID_H_
 
 #include <stdint.h>
+#include <functional>
 
 namespace tint {
 
diff --git a/include/tint/tint.h b/include/tint/tint.h
index c7f8a8c..57a3c67 100644
--- a/include/tint/tint.h
+++ b/include/tint/tint.h
@@ -15,6 +15,9 @@
 #ifndef INCLUDE_TINT_TINT_H_
 #define INCLUDE_TINT_TINT_H_
 
+// Guard for accidental includes to private headers
+#define CURRENTLY_IN_TINT_PUBLIC_HEADER
+
 // TODO(tint:88): When implementing support for an install target, all of these
 //                headers will need to be moved to include/tint/.
 
@@ -64,4 +67,16 @@
 #include "src/tint/writer/glsl/generator.h"
 #endif  // TINT_BUILD_GLSL_WRITER
 
+namespace tint {
+
+/// Initialize initializes the Tint library. Call before using the Tint API.
+void Initialize();
+
+/// Shutdown uninitializes the Tint library. Call after using the Tint API.
+void Shutdown();
+
+}  // namespace tint
+
+#undef CURRENTLY_IN_TINT_PUBLIC_HEADER
+
 #endif  // INCLUDE_TINT_TINT_H_
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 92dfe06..337b6a1 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -474,6 +474,7 @@
     "symbol_table.h",
     "text/unicode.cc",
     "text/unicode.h",
+    "tint.cc",
     "traits.h",
     "transform/add_block_attribute.cc",
     "transform/add_block_attribute.h",
@@ -580,6 +581,7 @@
     "utils/enum_set.h",
     "utils/foreach_macro.h",
     "utils/hash.h",
+    "utils/hashmap_base.h",
     "utils/hashmap.h",
     "utils/hashset.h",
     "utils/map.h",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index d4c13ca..a22d9fc 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -24,6 +24,36 @@
   )
 endfunction()
 
+set(TINT_LIB_SRCS)
+set(TINT_BENCHMARK_SRCS)
+set(TINT_TEST_SRCS)
+
+# Add a generated file set into the build.
+#
+# Params:
+#  TARGET - the target name to add (without extension)
+#  BENCH  - set if the target has a benchmark file
+#  TEST   - set if the target has a test file
+#
+function(tint_generated TARGET)
+  cmake_parse_arguments(PARSE_ARGV 0 TINT_GEN "BENCH;TEST" "" "")
+
+   list(APPEND TINT_LIB_SRCS
+      ${TARGET}.cc
+      ${TARGET}.h
+    )
+    set(TINT_LIB_SRCS ${TINT_LIB_SRCS} PARENT_SCOPE)
+
+    if(${TINT_GEN_BENCH})
+        list(APPEND TINT_BENCHMARK_SRCS ${TARGET}_bench.cc)
+        set(TINT_BENCHMARK_SRCS ${TINT_BENCHMARK_SRCS} PARENT_SCOPE)
+    endif()
+    if(${TINT_GEN_TEST})
+        list(APPEND TINT_TEST_SRCS ${TARGET}_test.cc)
+        set(TINT_TEST_SRCS ${TINT_TEST_SRCS} PARENT_SCOPE)
+    endif()
+endfunction()
+
 ## Tint diagnostic utilities. Used by libtint and tint_utils_io.
 add_library(tint_diagnostic_utils
   debug.cc
@@ -46,10 +76,8 @@
     PROPERTIES COMPILE_DEFINITIONS "TINT_ENABLE_BREAK_IN_DEBUGGER=1" )
 endif()
 
-set(TINT_LIB_SRCS
+list(APPEND TINT_LIB_SRCS
   ../../include/tint/tint.h
-  ast/access.cc
-  ast/access.h
   ast/alias.cc
   ast/alias.h
   ast/array.cc
@@ -79,8 +107,6 @@
   ast/break_statement.h
   ast/builtin_attribute.cc
   ast/builtin_attribute.h
-  ast/builtin_value.cc
-  ast/builtin_value.h
   ast/call_expression.cc
   ast/call_expression.h
   ast/call_statement.cc
@@ -107,8 +133,6 @@
   ast/enable.h
   ast/expression.cc
   ast/expression.h
-  ast/extension.cc
-  ast/extension.h
   ast/external_texture.cc
   ast/external_texture.h
   ast/f16.cc
@@ -141,8 +165,6 @@
   ast/int_literal_expression.h
   ast/internal_attribute.cc
   ast/internal_attribute.h
-  ast/interpolate_attribute.cc
-  ast/interpolate_attribute.h
   ast/invariant_attribute.cc
   ast/invariant_attribute.h
   ast/let.cc
@@ -186,8 +208,6 @@
   ast/statement.h
   ast/static_assert.cc
   ast/static_assert.h
-  ast/address_space.cc
-  ast/address_space.h
   ast/storage_texture.cc
   ast/storage_texture.h
   ast/stride_attribute.cc
@@ -204,8 +224,6 @@
   ast/struct.h
   ast/switch_statement.cc
   ast/switch_statement.h
-  ast/texel_format.cc
-  ast/texel_format.h
   ast/texture.cc
   ast/texture.h
   ast/traverse_expressions.h
@@ -248,26 +266,6 @@
   inspector/resource_binding.h
   inspector/scalar.cc
   inspector/scalar.h
-  ir/block.cc
-  ir/block.h
-  ir/builder.cc
-  ir/builder.h
-  ir/builder_impl.cc
-  ir/builder_impl.h
-  ir/flow_node.cc
-  ir/flow_node.h
-  ir/function.cc
-  ir/function.h
-  ir/if.cc
-  ir/if.h
-  ir/loop.cc
-  ir/loop.h
-  ir/module.cc
-  ir/module.h
-  ir/switch.cc
-  ir/switch.h
-  ir/terminator.cc
-  ir/terminator.h
   number.cc
   number.h
   program_builder.cc
@@ -283,8 +281,6 @@
   resolver/const_eval.h
   resolver/dependency_graph.cc
   resolver/dependency_graph.h
-  resolver/init_conv_intrinsic.cc
-  resolver/init_conv_intrinsic.h
   resolver/intrinsic_table.cc
   resolver/intrinsic_table.h
   resolver/intrinsic_table.inl
@@ -316,8 +312,6 @@
   sem/bool.h
   sem/break_if_statement.cc
   sem/break_if_statement.h
-  sem/builtin_type.cc
-  sem/builtin_type.h
   sem/builtin.cc
   sem/builtin.h
   sem/call_target.cc
@@ -363,8 +357,6 @@
   sem/multisampled_texture.h
   sem/node.cc
   sem/node.h
-  sem/parameter_usage.cc
-  sem/parameter_usage.h
   sem/pipeline_stage_set.h
   sem/pointer.cc
   sem/pointer.h
@@ -405,6 +397,7 @@
   symbol_table.h
   symbol.cc
   symbol.h
+  tint.cc
   text/unicode.cc
   text/unicode.h
   traits.h
@@ -511,6 +504,7 @@
   utils/enum_set.h
   utils/foreach_macro.h
   utils/hash.h
+  utils/hashmap_base.h
   utils/hashmap.h
   utils/hashset.h
   utils/map.h
@@ -541,6 +535,16 @@
   writer/writer.h
 )
 
+tint_generated(ast/access)
+tint_generated(ast/address_space BENCH TEST)
+tint_generated(ast/builtin_value BENCH TEST)
+tint_generated(ast/extension BENCH TEST)
+tint_generated(ast/interpolate_attribute)
+tint_generated(ast/texel_format BENCH TEST)
+tint_generated(resolver/init_conv_intrinsic)
+tint_generated(sem/builtin_type)
+tint_generated(sem/parameter_usage)
+
 if(UNIX)
   list(APPEND TINT_LIB_SRCS diagnostic/printer_linux.cc)
 elseif(WIN32)
@@ -644,6 +648,33 @@
   )
 endif()
 
+if(${TINT_BUILD_IR})
+  list(APPEND TINT_LIB_SRCS
+    ir/block.cc
+    ir/block.h
+    ir/builder.cc
+    ir/builder.h
+    ir/builder_impl.cc
+    ir/builder_impl.h
+    ir/debug.cc
+    ir/debug.h
+    ir/flow_node.cc
+    ir/flow_node.h
+    ir/function.cc
+    ir/function.h
+    ir/if.cc
+    ir/if.h
+    ir/loop.cc
+    ir/loop.h
+    ir/module.cc
+    ir/module.h
+    ir/switch.cc
+    ir/switch.h
+    ir/terminator.cc
+    ir/terminator.h
+  )
+endif()
+
 if(MSVC)
   list(APPEND TINT_LIB_SRCS
     tint.natvis
@@ -725,7 +756,7 @@
 # Tests
 ################################################################################
 if(TINT_BUILD_TESTS)
-  set(TINT_TEST_SRCS
+  list(APPEND TINT_TEST_SRCS
     ast/alias_test.cc
     ast/array_test.cc
     ast/assignment_statement_test.cc
@@ -741,7 +772,6 @@
     ast/builtin_attribute_test.cc
     ast/builtin_texture_helper_test.cc
     ast/builtin_texture_helper_test.h
-    ast/builtin_value_test.cc
     ast/call_expression_test.cc
     ast/call_statement_test.cc
     ast/case_selector_test.cc
@@ -752,7 +782,6 @@
     ast/depth_texture_test.cc
     ast/discard_statement_test.cc
     ast/enable_test.cc
-    ast/extension_test.cc
     ast/external_texture_test.cc
     ast/f16_test.cc
     ast/f32_test.cc
@@ -784,7 +813,6 @@
     ast/sampler_test.cc
     ast/stage_attribute_test.cc
     ast/static_assert_test.cc
-    ast/address_space_test.cc
     ast/storage_texture_test.cc
     ast/stride_attribute_test.cc
     ast/struct_member_align_attribute_test.cc
@@ -794,7 +822,6 @@
     ast/struct_test.cc
     ast/switch_statement_test.cc
     ast/test_helper.h
-    ast/texel_format_test.cc
     ast/texture_test.cc
     ast/traverse_expressions_test.cc
     ast/u32_test.cc
@@ -811,8 +838,6 @@
     diagnostic/diagnostic_test.cc
     diagnostic/formatter_test.cc
     diagnostic/printer_test.cc
-    ir/builder_impl_test.cc
-    ir/test_helper.h
     number_test.cc
     program_builder_test.cc
     program_test.cc
@@ -1162,9 +1187,9 @@
       transform/expand_compound_assignment_test.cc
       transform/first_index_offset_test.cc
       transform/for_loop_to_loop_test.cc
-      transform/expand_compound_assignment.cc
+      transform/expand_compound_assignment_test.cc
       transform/localize_struct_array_assignment_test.cc
-      transform/merge_return.cc
+      transform/merge_return_test.cc
       transform/module_scope_var_to_entry_point_param_test.cc
       transform/multiplanar_external_texture_test.cc
       transform/num_workgroups_from_uniform_test.cc
@@ -1305,6 +1330,13 @@
     )
   endif()
 
+  if (${TINT_BUILD_IR})
+    list(APPEND TINT_TEST_SRCS
+      ir/builder_impl_test.cc
+      ir/test_helper.h
+    )
+  endif()
+
   if (${TINT_BUILD_FUZZERS})
     list(APPEND TINT_TEST_SRCS
       fuzzers/mersenne_twister_engine.cc
@@ -1348,32 +1380,29 @@
     message(FATAL_ERROR "TINT_BUILD_BENCHMARKS requires TINT_BUILD_WGSL_READER")
   endif()
 
-  set(TINT_BENCHMARK_SRC
+  list(APPEND TINT_BENCHMARK_SRCS
     "castable_bench.cc"
-    "ast/extension_bench.cc"
-    "ast/address_space_bench.cc"
-    "ast/texel_format_bench.cc"
     "bench/benchmark.cc"
     "reader/wgsl/parser_bench.cc"
   )
 
   if (${TINT_BUILD_GLSL_WRITER})
-    list(APPEND TINT_BENCHMARK_SRC writer/glsl/generator_bench.cc)
+    list(APPEND TINT_BENCHMARK_SRCS writer/glsl/generator_bench.cc)
   endif()
   if (${TINT_BUILD_HLSL_WRITER})
-    list(APPEND TINT_BENCHMARK_SRC writer/hlsl/generator_bench.cc)
+    list(APPEND TINT_BENCHMARK_SRCS writer/hlsl/generator_bench.cc)
   endif()
   if (${TINT_BUILD_MSL_WRITER})
-    list(APPEND TINT_BENCHMARK_SRC writer/msl/generator_bench.cc)
+    list(APPEND TINT_BENCHMARK_SRCS writer/msl/generator_bench.cc)
   endif()
   if (${TINT_BUILD_SPV_WRITER})
-    list(APPEND TINT_BENCHMARK_SRC writer/spirv/generator_bench.cc)
+    list(APPEND TINT_BENCHMARK_SRCS writer/spirv/generator_bench.cc)
   endif()
   if (${TINT_BUILD_WGSL_WRITER})
-    list(APPEND TINT_BENCHMARK_SRC writer/wgsl/generator_bench.cc)
+    list(APPEND TINT_BENCHMARK_SRCS writer/wgsl/generator_bench.cc)
   endif()
 
-  add_executable(tint-benchmark ${TINT_BENCHMARK_SRC})
+  add_executable(tint-benchmark ${TINT_BENCHMARK_SRCS})
   set_target_properties(${target} PROPERTIES FOLDER "Benchmarks")
 
   tint_core_compile_options(tint-benchmark)
diff --git a/src/tint/clone_context.h b/src/tint/clone_context.h
index 8e045f5..05f4868 100644
--- a/src/tint/clone_context.h
+++ b/src/tint/clone_context.h
@@ -27,6 +27,7 @@
 #include "src/tint/symbol.h"
 #include "src/tint/traits.h"
 #include "src/tint/utils/hashmap.h"
+#include "src/tint/utils/hashset.h"
 #include "src/tint/utils/vector.h"
 
 // Forward declarations
@@ -203,26 +204,26 @@
         auto transforms = list_transforms_.Find(&from);
 
         if (transforms) {
-            for (auto* o : transforms->insert_front_) {
-                to.Push(CheckedCast<T>(o));
+            for (auto& builder : transforms->insert_front_) {
+                to.Push(CheckedCast<T>(builder()));
             }
             for (auto& el : from) {
                 if (auto* insert_before = transforms->insert_before_.Find(el)) {
-                    for (auto insert : *insert_before) {
-                        to.Push(CheckedCast<T>(insert));
+                    for (auto& builder : *insert_before) {
+                        to.Push(CheckedCast<T>(builder()));
                     }
                 }
                 if (!transforms->remove_.Contains(el)) {
                     to.Push(Clone(el));
                 }
                 if (auto* insert_after = transforms->insert_after_.Find(el)) {
-                    for (auto insert : *insert_after) {
-                        to.Push(CheckedCast<T>(insert));
+                    for (auto& builder : *insert_after) {
+                        to.Push(CheckedCast<T>(builder()));
                     }
                 }
             }
-            for (auto* o : transforms->insert_back_) {
-                to.Push(CheckedCast<T>(o));
+            for (auto& builder : transforms->insert_back_) {
+                to.Push(CheckedCast<T>(builder()));
             }
         } else {
             for (auto& el : from) {
@@ -232,8 +233,8 @@
                 // transform for `from`.
                 if (transforms) {
                     if (auto* insert_after = transforms->insert_after_.Find(el)) {
-                        for (auto insert : *insert_after) {
-                            to.Push(CheckedCast<T>(insert));
+                        for (auto& builder : *insert_after) {
+                            to.Push(CheckedCast<T>(builder()));
                         }
                     }
                 }
@@ -242,8 +243,8 @@
             // Clone(el) may have updated the transformation list, adding an `insert_back_`
             // transform for `from`.
             if (transforms) {
-                for (auto* o : transforms->insert_back_) {
-                    to.Push(CheckedCast<T>(o));
+                for (auto& builder : transforms->insert_back_) {
+                    to.Push(CheckedCast<T>(builder()));
                 }
             }
         }
@@ -374,7 +375,7 @@
         return *this;
     }
 
-    /// Removes `object` from the cloned copy of `vector`.
+    /// Removes @p object from the cloned copy of @p vector.
     /// @param vector the vector in #src
     /// @param object a pointer to the object in #src that will be omitted from
     /// the cloned vector.
@@ -392,7 +393,7 @@
         return *this;
     }
 
-    /// Inserts `object` before any other objects of `vector`, when it is cloned.
+    /// Inserts @p object before any other objects of @p vector, when the vector is cloned.
     /// @param vector the vector in #src
     /// @param object a pointer to the object in #dst that will be inserted at the
     /// front of the vector
@@ -400,11 +401,21 @@
     template <typename T, size_t N, typename OBJECT>
     CloneContext& InsertFront(const utils::Vector<T, N>& vector, OBJECT* object) {
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, dst, object);
-        list_transforms_.Edit(&vector).insert_front_.Push(object);
+        return InsertFront(vector, [object] { return object; });
+    }
+
+    /// Inserts a lazily built object before any other objects of @p vector, when the vector is
+    /// cloned.
+    /// @param vector the vector in #src
+    /// @param builder a builder of the object that will be inserted at the front of the vector.
+    /// @returns this CloneContext so calls can be chained
+    template <typename T, size_t N, typename BUILDER>
+    CloneContext& InsertFront(const utils::Vector<T, N>& vector, BUILDER&& builder) {
+        list_transforms_.Edit(&vector).insert_front_.Push(std::forward<BUILDER>(builder));
         return *this;
     }
 
-    /// Inserts `object` after any other objects of `vector`, when it is cloned.
+    /// Inserts @p object after any other objects of @p vector, when the vector is cloned.
     /// @param vector the vector in #src
     /// @param object a pointer to the object in #dst that will be inserted at the
     /// end of the vector
@@ -412,15 +423,26 @@
     template <typename T, size_t N, typename OBJECT>
     CloneContext& InsertBack(const utils::Vector<T, N>& vector, OBJECT* object) {
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, dst, object);
-        list_transforms_.Edit(&vector).insert_back_.Push(object);
+        return InsertBack(vector, [object] { return object; });
+    }
+
+    /// Inserts a lazily built object after any other objects of @p vector, when the vector is
+    /// cloned.
+    /// @param vector the vector in #src
+    /// @param builder the builder of the object in #dst that will be inserted at the end of the
+    /// vector.
+    /// @returns this CloneContext so calls can be chained
+    template <typename T, size_t N, typename BUILDER>
+    CloneContext& InsertBack(const utils::Vector<T, N>& vector, BUILDER&& builder) {
+        list_transforms_.Edit(&vector).insert_back_.Push(std::forward<BUILDER>(builder));
         return *this;
     }
 
-    /// Inserts `object` before `before` whenever `vector` is cloned.
+    /// Inserts @p object before @p before whenever @p vector is cloned.
     /// @param vector the vector in #src
     /// @param before a pointer to the object in #src
     /// @param object a pointer to the object in #dst that will be inserted before
-    /// any occurrence of the clone of `before`
+    /// any occurrence of the clone of @p before
     /// @returns this CloneContext so calls can be chained
     template <typename T, size_t N, typename BEFORE, typename OBJECT>
     CloneContext& InsertBefore(const utils::Vector<T, N>& vector,
@@ -434,15 +456,35 @@
             return *this;
         }
 
-        list_transforms_.Edit(&vector).insert_before_.GetOrZero(before).Push(object);
+        list_transforms_.Edit(&vector).insert_before_.GetOrZero(before).Push(
+            [object] { return object; });
         return *this;
     }
 
-    /// Inserts `object` after `after` whenever `vector` is cloned.
+    /// Inserts a lazily created object before @p before whenever @p vector is cloned.
+    /// @param vector the vector in #src
+    /// @param before a pointer to the object in #src
+    /// @param builder the builder of the object in #dst that will be inserted before any occurrence
+    /// of the clone of @p before
+    /// @returns this CloneContext so calls can be chained
+    template <typename T,
+              size_t N,
+              typename BEFORE,
+              typename BUILDER,
+              typename _ = std::enable_if_t<!std::is_pointer_v<std::decay_t<BUILDER>>>>
+    CloneContext& InsertBefore(const utils::Vector<T, N>& vector,
+                               const BEFORE* before,
+                               BUILDER&& builder) {
+        list_transforms_.Edit(&vector).insert_before_.GetOrZero(before).Push(
+            std::forward<BUILDER>(builder));
+        return *this;
+    }
+
+    /// Inserts @p object after @p after whenever @p vector is cloned.
     /// @param vector the vector in #src
     /// @param after a pointer to the object in #src
     /// @param object a pointer to the object in #dst that will be inserted after
-    /// any occurrence of the clone of `after`
+    /// any occurrence of the clone of @p after
     /// @returns this CloneContext so calls can be chained
     template <typename T, size_t N, typename AFTER, typename OBJECT>
     CloneContext& InsertAfter(const utils::Vector<T, N>& vector,
@@ -456,7 +498,27 @@
             return *this;
         }
 
-        list_transforms_.Edit(&vector).insert_after_.GetOrZero(after).Push(object);
+        list_transforms_.Edit(&vector).insert_after_.GetOrZero(after).Push(
+            [object] { return object; });
+        return *this;
+    }
+
+    /// Inserts a lazily created object after @p after whenever @p vector is cloned.
+    /// @param vector the vector in #src
+    /// @param after a pointer to the object in #src
+    /// @param builder the builder of the object in #dst that will be inserted after any occurrence
+    /// of the clone of @p after
+    /// @returns this CloneContext so calls can be chained
+    template <typename T,
+              size_t N,
+              typename AFTER,
+              typename BUILDER,
+              typename _ = std::enable_if_t<!std::is_pointer_v<std::decay_t<BUILDER>>>>
+    CloneContext& InsertAfter(const utils::Vector<T, N>& vector,
+                              const AFTER* after,
+                              BUILDER&& builder) {
+        list_transforms_.Edit(&vector).insert_after_.GetOrZero(after).Push(
+            std::forward<BUILDER>(builder));
         return *this;
     }
 
@@ -487,7 +549,7 @@
     };
 
     /// A vector of const Cloneable*
-    using CloneableList = utils::Vector<const Cloneable*, 4>;
+    using CloneableBuilderList = utils::Vector<std::function<const Cloneable*()>, 4>;
 
     /// Transformations to be applied to a list (vector)
     struct ListTransforms {
@@ -495,20 +557,20 @@
         utils::Hashset<const Cloneable*, 4> remove_;
 
         /// A list of objects in #dst to insert before any others when the vector is cloned.
-        CloneableList insert_front_;
+        CloneableBuilderList insert_front_;
 
         /// A list of objects in #dst to insert after all others when the vector is cloned.
-        CloneableList insert_back_;
+        CloneableBuilderList insert_back_;
 
         /// A map of object in #src to the list of cloned objects in #dst.
         /// Clone(const utils::Vector<T*>& v) will use this to insert the map-value
         /// list into the target vector before cloning and inserting the map-key.
-        utils::Hashmap<const Cloneable*, CloneableList, 4> insert_before_;
+        utils::Hashmap<const Cloneable*, CloneableBuilderList, 4> insert_before_;
 
         /// A map of object in #src to the list of cloned objects in #dst.
         /// Clone(const utils::Vector<T*>& v) will use this to insert the map-value
         /// list into the target vector after cloning and inserting the map-key.
-        utils::Hashmap<const Cloneable*, CloneableList, 4> insert_after_;
+        utils::Hashmap<const Cloneable*, CloneableBuilderList, 4> insert_after_;
     };
 
     CloneContext(const CloneContext&) = delete;
diff --git a/src/tint/clone_context_test.cc b/src/tint/clone_context_test.cc
index c895151..f2ed247 100644
--- a/src/tint/clone_context_test.cc
+++ b/src/tint/clone_context_test.cc
@@ -430,6 +430,39 @@
     EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertFrontFunction) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec = {
+        a.Create<Node>(builder.Symbols().Register("a")),
+        a.Create<Node>(builder.Symbols().Register("b")),
+        a.Create<Node>(builder.Symbols().Register("c")),
+    };
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+
+    auto* cloned_root =
+        CloneContext(&cloned, &original)
+            .InsertFront(original_root->vec,
+                         [&] { return a.Create<Node>(cloned.Symbols().New("insertion")); })
+            .Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 4u);
+
+    EXPECT_NE(cloned_root->vec[0], cloned_root->a);
+    EXPECT_NE(cloned_root->vec[1], cloned_root->b);
+    EXPECT_NE(cloned_root->vec[2], cloned_root->c);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion"));
+    EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("a"));
+    EXPECT_EQ(cloned_root->vec[2]->name, cloned.Symbols().Get("b"));
+    EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertFront_Empty) {
     Allocator a;
 
@@ -451,6 +484,28 @@
     EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertFront_Empty_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec.Clear();
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+
+    auto* cloned_root =
+        CloneContext(&cloned, &original)
+            .InsertFront(original_root->vec,
+                         [&] { return a.Create<Node>(cloned.Symbols().New("insertion")); })
+            .Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 1u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertBack) {
     Allocator a;
 
@@ -479,6 +534,35 @@
     EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("insertion"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertBack_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec = {
+        a.Create<Node>(builder.Symbols().Register("a")),
+        a.Create<Node>(builder.Symbols().Register("b")),
+        a.Create<Node>(builder.Symbols().Register("c")),
+    };
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+
+    auto* cloned_root =
+        CloneContext(&cloned, &original)
+            .InsertBack(original_root->vec,
+                        [&] { return a.Create<Node>(cloned.Symbols().New("insertion")); })
+            .Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 4u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("a"));
+    EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("b"));
+    EXPECT_EQ(cloned_root->vec[2]->name, cloned.Symbols().Get("c"));
+    EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("insertion"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertBack_Empty) {
     Allocator a;
 
@@ -500,6 +584,28 @@
     EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertBack_Empty_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec.Clear();
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+
+    auto* cloned_root =
+        CloneContext(&cloned, &original)
+            .InsertBack(original_root->vec,
+                        [&] { return a.Create<Node>(cloned.Symbols().New("insertion")); })
+            .Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 1u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertFrontAndBack_Empty) {
     Allocator a;
 
@@ -509,8 +615,8 @@
     Program original(std::move(builder));
 
     ProgramBuilder cloned;
-    auto* insertion_front = a.Create<Node>(cloned.Symbols().New("insertion_front"));
     auto* insertion_back = a.Create<Node>(cloned.Symbols().New("insertion_back"));
+    auto* insertion_front = a.Create<Node>(cloned.Symbols().New("insertion_front"));
 
     auto* cloned_root = CloneContext(&cloned, &original)
                             .InsertBack(original_root->vec, insertion_back)
@@ -524,6 +630,31 @@
     EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("insertion_back"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertFrontAndBack_Empty_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec.Clear();
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+
+    auto* cloned_root =
+        CloneContext(&cloned, &original)
+            .InsertBack(original_root->vec,
+                        [&] { return a.Create<Node>(cloned.Symbols().New("insertion_back")); })
+            .InsertFront(original_root->vec,
+                         [&] { return a.Create<Node>(cloned.Symbols().New("insertion_front")); })
+            .Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 2u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("insertion_front"));
+    EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("insertion_back"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertBefore) {
     Allocator a;
 
@@ -552,6 +683,35 @@
     EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertBefore_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec = {
+        a.Create<Node>(builder.Symbols().Register("a")),
+        a.Create<Node>(builder.Symbols().Register("b")),
+        a.Create<Node>(builder.Symbols().Register("c")),
+    };
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+
+    auto* cloned_root =
+        CloneContext(&cloned, &original)
+            .InsertBefore(original_root->vec, original_root->vec[1],
+                          [&] { return a.Create<Node>(cloned.Symbols().New("insertion")); })
+            .Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 4u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("a"));
+    EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("insertion"));
+    EXPECT_EQ(cloned_root->vec[2]->name, cloned.Symbols().Get("b"));
+    EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertAfter) {
     Allocator a;
 
@@ -580,6 +740,35 @@
     EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertAfter_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec = {
+        a.Create<Node>(builder.Symbols().Register("a")),
+        a.Create<Node>(builder.Symbols().Register("b")),
+        a.Create<Node>(builder.Symbols().Register("c")),
+    };
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+
+    auto* cloned_root =
+        CloneContext(&cloned, &original)
+            .InsertAfter(original_root->vec, original_root->vec[1],
+                         [&] { return a.Create<Node>(cloned.Symbols().New("insertion")); })
+            .Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 4u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("a"));
+    EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("b"));
+    EXPECT_EQ(cloned_root->vec[2]->name, cloned.Symbols().Get("insertion"));
+    EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertAfterInVectorNodeClone) {
     Allocator a;
 
@@ -612,6 +801,38 @@
     EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertAfterInVectorNodeClone_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec = {
+        a.Create<Node>(builder.Symbols().Register("a")),
+        a.Create<Replaceable>(builder.Symbols().Register("b")),
+        a.Create<Node>(builder.Symbols().Register("c")),
+    };
+
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+    CloneContext ctx(&cloned, &original);
+    ctx.ReplaceAll([&](const Replaceable* r) {
+        ctx.InsertAfter(original_root->vec, r,
+                        [&] { return a.Create<Node>(cloned.Symbols().New("insertion")); });
+        return nullptr;
+    });
+
+    auto* cloned_root = ctx.Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 4u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("a"));
+    EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("b"));
+    EXPECT_EQ(cloned_root->vec[2]->name, cloned.Symbols().Get("insertion"));
+    EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertBackInVectorNodeClone) {
     Allocator a;
 
@@ -644,6 +865,38 @@
     EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("insertion"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertBackInVectorNodeClone_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec = {
+        a.Create<Node>(builder.Symbols().Register("a")),
+        a.Create<Replaceable>(builder.Symbols().Register("b")),
+        a.Create<Node>(builder.Symbols().Register("c")),
+    };
+
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+    CloneContext ctx(&cloned, &original);
+    ctx.ReplaceAll([&](const Replaceable* /*r*/) {
+        ctx.InsertBack(original_root->vec,
+                       [&] { return a.Create<Node>(cloned.Symbols().New("insertion")); });
+        return nullptr;
+    });
+
+    auto* cloned_root = ctx.Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 4u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("a"));
+    EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("b"));
+    EXPECT_EQ(cloned_root->vec[2]->name, cloned.Symbols().Get("c"));
+    EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("insertion"));
+}
+
 TEST_F(CloneContextNodeTest, CloneWithInsertBeforeAndAfterRemoved) {
     Allocator a;
 
@@ -676,6 +929,38 @@
     EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
 }
 
+TEST_F(CloneContextNodeTest, CloneWithInsertBeforeAndAfterRemoved_Function) {
+    Allocator a;
+
+    ProgramBuilder builder;
+    auto* original_root = a.Create<Node>(builder.Symbols().Register("root"));
+    original_root->vec = {
+        a.Create<Node>(builder.Symbols().Register("a")),
+        a.Create<Node>(builder.Symbols().Register("b")),
+        a.Create<Node>(builder.Symbols().Register("c")),
+    };
+    Program original(std::move(builder));
+
+    ProgramBuilder cloned;
+
+    auto* cloned_root =
+        CloneContext(&cloned, &original)
+            .InsertBefore(original_root->vec, original_root->vec[1],
+                          [&] { return a.Create<Node>(cloned.Symbols().New("insertion_before")); })
+            .InsertAfter(original_root->vec, original_root->vec[1],
+                         [&] { return a.Create<Node>(cloned.Symbols().New("insertion_after")); })
+            .Remove(original_root->vec, original_root->vec[1])
+            .Clone(original_root);
+
+    EXPECT_EQ(cloned_root->vec.Length(), 4u);
+
+    EXPECT_EQ(cloned_root->name, cloned.Symbols().Get("root"));
+    EXPECT_EQ(cloned_root->vec[0]->name, cloned.Symbols().Get("a"));
+    EXPECT_EQ(cloned_root->vec[1]->name, cloned.Symbols().Get("insertion_before"));
+    EXPECT_EQ(cloned_root->vec[2]->name, cloned.Symbols().Get("insertion_after"));
+    EXPECT_EQ(cloned_root->vec[3]->name, cloned.Symbols().Get("c"));
+}
+
 TEST_F(CloneContextNodeTest, CloneIntoSameBuilder) {
     ProgramBuilder builder;
     CloneContext ctx(&builder);
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index f27e643..485ca5a 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -26,7 +26,7 @@
 #if TINT_BUILD_GLSL_WRITER
 #include "StandAlone/ResourceLimits.h"
 #include "glslang/Public/ShaderLang.h"
-#endif
+#endif  // TINT_BUILD_GLSL_WRITER
 
 #if TINT_BUILD_SPV_READER
 #include "spirv-tools/libspirv.hpp"
@@ -39,6 +39,11 @@
 #include "src/tint/val/val.h"
 #include "tint/tint.h"
 
+#if TINT_BUILD_IR
+#include "src/tint/ir/debug.h"
+#include "src/tint/ir/module.h"
+#endif  // TINT_BUILD_IR
+
 namespace {
 
 [[noreturn]] void TintInternalCompilerErrorReporter(const tint::diag::List& diagnostics) {
@@ -85,6 +90,8 @@
     bool emit_single_entry_point = false;
     std::string ep_name;
 
+    bool rename_all = false;
+
     std::vector<std::string> transforms;
 
     std::string fxc_path;
@@ -92,6 +99,10 @@
     std::string xcrun_path;
     std::unordered_map<std::string, double> overrides;
     std::optional<tint::sem::BindingPoint> hlsl_root_constant_binding_point;
+
+#if TINT_BUILD_IR
+    bool dump_ir_graph = false;
+#endif  // TINT_BUILD_IR
 };
 
 const char kUsage[] = R"(Usage: tint [options] <input-file>
@@ -131,6 +142,7 @@
   --xcrun                   -- Path to xcrun executable, used to validate MSL output.
                                When specified, automatically enables MSL validation
   --overrides               -- Override values as IDENTIFIER=VALUE, comma-separated.
+  --rename-all              -- Renames all symbols.
 )";
 
 Format parse_format(const std::string& fmt) {
@@ -433,6 +445,10 @@
                 return false;
             }
             opts->dxc_path = args[i];
+#if TINT_BUILD_IR
+        } else if (arg == "--dump-ir-graph") {
+            opts->dump_ir_graph = true;
+#endif  // TINT_BUILD_IR
         } else if (arg == "--xcrun") {
             ++i;
             if (i >= args.size()) {
@@ -451,6 +467,9 @@
                 auto parts = split_on_equal(o);
                 opts->overrides.insert({parts[0], std::stod(parts[1])});
             }
+        } else if (arg == "--rename-all") {
+            ++i;
+            opts->rename_all = true;
         } else if (arg == "--hlsl-root-constant-binding-point") {
             ++i;
             if (i >= args.size()) {
@@ -1129,6 +1148,11 @@
 
     if (options.show_help) {
         std::string usage = tint::utils::ReplaceAll(kUsage, "${transforms}", transform_names());
+#if TINT_BUILD_IR
+        usage +=
+            "  --dump-ir-graph           -- Writes the IR graph to 'tint.dot' as a dot graph\n";
+#endif  // TINT_BUILD_IR
+
         std::cout << usage << std::endl;
         return 0;
     }
@@ -1247,6 +1271,19 @@
         return 1;
     }
 
+#if TINT_BUILD_IR
+    if (options.dump_ir_graph) {
+        auto result = tint::ir::Module::FromProgram(program.get());
+        if (!result) {
+            std::cerr << "Failed to build IR from program: " << result.Failure() << std::endl;
+        } else {
+            auto mod = result.Move();
+            auto graph = tint::ir::Debug::AsDotGraph(&mod);
+            WriteFile("tint.dot", "w", graph);
+        }
+    }
+#endif  // TINT_BUILD_IR
+
     tint::inspector::Inspector inspector(program.get());
 
     if (options.dump_inspector_bindings) {
@@ -1284,50 +1321,13 @@
     tint::transform::Manager transform_manager;
     tint::transform::DataMap transform_inputs;
 
-    // If overrides are provided, add the SubstituteOverride transform.
-    if (!options.overrides.empty()) {
-        for (auto& t : transforms) {
-            if (t.name == std::string("substitute_override")) {
-                if (!t.make(inspector, transform_manager, transform_inputs)) {
-                    return 1;
-                }
-                break;
-            }
-        }
-    }
-
-    for (const auto& name : options.transforms) {
-        // TODO(dsinclair): The vertex pulling transform requires setup code to
-        // be run that needs user input. Should we find a way to support that here
-        // maybe through a provided file?
-
-        bool found = false;
-        for (auto& t : transforms) {
-            if (t.name == name) {
-                if (!t.make(inspector, transform_manager, transform_inputs)) {
-                    return 1;
-                }
-                found = true;
-                break;
-            }
-        }
-        if (!found) {
-            std::cerr << "Unknown transform: " << name << std::endl;
-            std::cerr << "Available transforms: " << std::endl << transform_names();
-            return 1;
-        }
-    }
-
-    if (options.emit_single_entry_point) {
-        transform_manager.append(std::make_unique<tint::transform::SingleEntryPoint>());
-        transform_inputs.Add<tint::transform::SingleEntryPoint::Config>(options.ep_name);
-    }
-
+    // Renaming must always come first
     switch (options.format) {
         case Format::kMsl: {
 #if TINT_BUILD_MSL_WRITER
             transform_inputs.Add<tint::transform::Renamer::Config>(
-                tint::transform::Renamer::Target::kMslKeywords,
+                options.rename_all ? tint::transform::Renamer::Target::kAll
+                                   : tint::transform::Renamer::Target::kMslKeywords,
                 /* preserve_unicode */ false);
             transform_manager.Add<tint::transform::Renamer>();
 #endif  // TINT_BUILD_MSL_WRITER
@@ -1335,20 +1335,63 @@
         }
 #if TINT_BUILD_GLSL_WRITER
         case Format::kGlsl: {
+            transform_inputs.Add<tint::transform::Renamer::Config>(
+                options.rename_all ? tint::transform::Renamer::Target::kAll
+                                   : tint::transform::Renamer::Target::kGlslKeywords,
+                /* preserve_unicode */ false);
+            transform_manager.Add<tint::transform::Renamer>();
             break;
         }
 #endif  // TINT_BUILD_GLSL_WRITER
         case Format::kHlsl: {
 #if TINT_BUILD_HLSL_WRITER
             transform_inputs.Add<tint::transform::Renamer::Config>(
-                tint::transform::Renamer::Target::kHlslKeywords,
+                options.rename_all ? tint::transform::Renamer::Target::kAll
+                                   : tint::transform::Renamer::Target::kHlslKeywords,
                 /* preserve_unicode */ false);
             transform_manager.Add<tint::transform::Renamer>();
 #endif  // TINT_BUILD_HLSL_WRITER
             break;
         }
-        default:
+        default: {
+            if (options.rename_all) {
+                transform_manager.Add<tint::transform::Renamer>();
+            }
             break;
+        }
+    }
+
+    auto enable_transform = [&](std::string_view name) {
+        for (auto& t : transforms) {
+            if (t.name == name) {
+                return t.make(inspector, transform_manager, transform_inputs);
+            }
+        }
+
+        std::cerr << "Unknown transform: " << name << std::endl;
+        std::cerr << "Available transforms: " << std::endl << transform_names();
+        return false;
+    };
+
+    // If overrides are provided, add the SubstituteOverride transform.
+    if (!options.overrides.empty()) {
+        if (!enable_transform("substitute_override")) {
+            return 1;
+        }
+    }
+
+    for (const auto& name : options.transforms) {
+        // TODO(dsinclair): The vertex pulling transform requires setup code to
+        // be run that needs user input. Should we find a way to support that here
+        // maybe through a provided file?
+        if (!enable_transform(name)) {
+            return 1;
+        }
+    }
+
+    if (options.emit_single_entry_point) {
+        transform_manager.append(std::make_unique<tint::transform::SingleEntryPoint>());
+        transform_inputs.Add<tint::transform::SingleEntryPoint::Config>(options.ep_name);
     }
 
     auto out = transform_manager.Run(program.get(), std::move(transform_inputs));
diff --git a/src/tint/fuzzers/shuffle_transform.cc b/src/tint/fuzzers/shuffle_transform.cc
index 5f5f6e6..6ae405a 100644
--- a/src/tint/fuzzers/shuffle_transform.cc
+++ b/src/tint/fuzzers/shuffle_transform.cc
@@ -15,6 +15,7 @@
 #include "src/tint/fuzzers/shuffle_transform.h"
 
 #include <random>
+#include <utility>
 
 #include "src/tint/program_builder.h"
 
@@ -22,15 +23,21 @@
 
 ShuffleTransform::ShuffleTransform(size_t seed) : seed_(seed) {}
 
-void ShuffleTransform::Run(CloneContext& ctx,
-                           const tint::transform::DataMap&,
-                           tint::transform::DataMap&) const {
-    auto decls = ctx.src->AST().GlobalDeclarations();
+transform::Transform::ApplyResult ShuffleTransform::Apply(const Program* src,
+                                                          const transform::DataMap&,
+                                                          transform::DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
+    auto decls = src->AST().GlobalDeclarations();
     auto rng = std::mt19937_64{seed_};
     std::shuffle(std::begin(decls), std::end(decls), rng);
     for (auto* decl : decls) {
-        ctx.dst->AST().AddGlobalDeclaration(ctx.Clone(decl));
+        b.AST().AddGlobalDeclaration(ctx.Clone(decl));
     }
+
+    ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::fuzzers
diff --git a/src/tint/fuzzers/shuffle_transform.h b/src/tint/fuzzers/shuffle_transform.h
index 0a64fe3..ee54f97 100644
--- a/src/tint/fuzzers/shuffle_transform.h
+++ b/src/tint/fuzzers/shuffle_transform.h
@@ -20,16 +20,16 @@
 namespace tint::fuzzers {
 
 /// ShuffleTransform reorders the module scope declarations into a random order
-class ShuffleTransform : public tint::transform::Transform {
+class ShuffleTransform : public transform::Transform {
   public:
     /// Constructor
     /// @param seed the random seed to use for the shuffling
     explicit ShuffleTransform(size_t seed);
 
-  protected:
-    void Run(CloneContext& ctx,
-             const tint::transform::DataMap&,
-             tint::transform::DataMap&) const override;
+    /// @copydoc transform::Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const transform::DataMap& inputs,
+                      transform::DataMap& outputs) const override;
 
   private:
     size_t seed_;
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 9a6bae2..f497405 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -404,14 +404,14 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 // https://gpuweb.github.io/gpuweb/wgsl/#builtin-functions
-fn abs<T: fiu32_f16>(T) -> T
-fn abs<N: num, T: fiu32_f16>(vec<N, T>) -> vec<N, T>
-fn acos<T: f32_f16>(T) -> T
-fn acos<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn abs<T: fia_fiu32_f16>(T) -> T
+@const fn abs<N: num, T: fia_fiu32_f16>(vec<N, T>) -> vec<N, T>
+@const fn acos<T: fa_f32_f16>(@test_value(0.87758256189) T) -> T
+@const fn acos<N: num, T: fa_f32_f16>(@test_value(0.87758256189) vec<N, T>) -> vec<N, T>
 fn acosh<T: f32_f16>(T) -> T
 fn acosh<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn all(bool) -> bool
-fn all<N: num>(vec<N, bool>) -> bool
+@const fn all(bool) -> bool
+@const fn all<N: num>(vec<N, bool>) -> bool
 @const fn any(bool) -> bool
 @const fn any<N: num>(vec<N, bool>) -> bool
 fn arrayLength<T, A: access>(ptr<storage, array<T>, A>) -> u32
@@ -425,8 +425,8 @@
 @const fn atan2<T: fa_f32_f16, N: num>(vec<N, T>, vec<N, T>) -> vec<N, T>
 @const fn atanh<T: fa_f32_f16>(@test_value(0.5) T) -> T
 @const fn atanh<N: num, T: fa_f32_f16>(@test_value(0.5) vec<N, T>) -> vec<N, T>
-fn ceil<T: f32_f16>(T) -> T
-fn ceil<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn ceil<T: fa_f32_f16>(@test_value(1.5) T) -> T
+@const fn ceil<N: num, T: fa_f32_f16>(@test_value(1.5) vec<N, T>) -> vec<N, T>
 @const fn clamp<T: fia_fiu32_f16>(T, T, T) -> T
 @const fn clamp<T: fia_fiu32_f16, N: num>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T>
 fn cos<T: f32_f16>(T) -> T
@@ -464,15 +464,15 @@
 fn exp<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
 fn exp2<T: f32_f16>(T) -> T
 fn exp2<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
-fn extractBits<T: iu32>(T, u32, u32) -> T
-fn extractBits<N: num, T: iu32>(vec<N, T>, u32, u32) -> vec<N, T>
+@const fn extractBits<T: iu32>(T, u32, u32) -> T
+@const fn extractBits<N: num, T: iu32>(vec<N, T>, u32, u32) -> vec<N, T>
 fn faceForward<N: num, T: f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T>
 @const fn firstLeadingBit<T: iu32>(T) -> T
 @const fn firstLeadingBit<N: num, T: iu32>(vec<N, T>) -> vec<N, T>
 @const fn firstTrailingBit<T: iu32>(T) -> T
 @const fn firstTrailingBit<N: num, T: iu32>(vec<N, T>) -> vec<N, T>
-fn floor<T: f32_f16>(T) -> T
-fn floor<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
+@const fn floor<T: fa_f32_f16>(@test_value(1.5) T) -> T
+@const fn floor<N: num, T: fa_f32_f16>(@test_value(1.5) vec<N, T>) -> vec<N, T>
 fn fma<T: f32_f16>(T, T, T) -> T
 fn fma<N: num, T: f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T>
 fn fract<T: f32_f16>(T) -> T
@@ -485,8 +485,8 @@
 @stage("fragment") fn fwidthCoarse<N: num>(vec<N, f32>) -> vec<N, f32>
 @stage("fragment") fn fwidthFine(f32) -> f32
 @stage("fragment") fn fwidthFine<N: num>(vec<N, f32>) -> vec<N, f32>
-fn insertBits<T: iu32>(T, T, u32, u32) -> T
-fn insertBits<N: num, T: iu32>(vec<N, T>, vec<N, T>, u32, u32) -> vec<N, T>
+@const fn insertBits<T: iu32>(T, T, u32, u32) -> T
+@const fn insertBits<N: num, T: iu32>(vec<N, T>, vec<N, T>, u32, u32) -> vec<N, T>
 fn inverseSqrt<T: f32_f16>(T) -> T
 fn inverseSqrt<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
 fn ldexp<T: f32_f16>(T, i32) -> T
@@ -514,12 +514,14 @@
 fn pack4x8unorm(vec4<f32>) -> u32
 fn pow<T: f32_f16>(T, T) -> T
 fn pow<N: num, T: f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T>
+@const fn quantizeToF16(f32) -> f32
+@const fn quantizeToF16<N: num>(vec<N, f32>) -> vec<N, f32>
 fn radians<T: f32_f16>(T) -> T
 fn radians<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
 fn reflect<N: num, T: f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T>
 fn refract<N: num, T: f32_f16>(vec<N, T>, vec<N, T>, T) -> vec<N, T>
-fn reverseBits<T: iu32>(T) -> T
-fn reverseBits<N: num, T: iu32>(vec<N, T>) -> vec<N, T>
+@const fn reverseBits<T: iu32>(T) -> T
+@const fn reverseBits<N: num, T: iu32>(vec<N, T>) -> vec<N, T>
 fn round<T: f32_f16>(T) -> T
 fn round<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
 @const fn saturate<T: fa_f32_f16>(@test_value(2) T) -> T
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index 66b195b..851d2d6 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -39,35 +39,58 @@
     auto* ir_func = ir.flow_nodes.Create<Function>(ast_func);
     ir_func->start_target = CreateBlock();
     ir_func->end_target = CreateTerminator();
+
+    // Function is always branching into the start target
+    ir_func->start_target->inbound_branches.Push(ir_func);
+
     return ir_func;
 }
 
-If* Builder::CreateIf(const ast::Statement* stmt, IfFlags flags) {
+If* Builder::CreateIf(const ast::Statement* stmt) {
     auto* ir_if = ir.flow_nodes.Create<If>(stmt);
-    ir_if->false_target = CreateBlock();
     ir_if->true_target = CreateBlock();
+    ir_if->false_target = CreateBlock();
+    ir_if->merge_target = CreateBlock();
 
-    if (flags == IfFlags::kCreateMerge) {
-        ir_if->merge_target = CreateBlock();
-    } else {
-        ir_if->merge_target = nullptr;
-    }
+    // An if always branches to both the true and false block.
+    ir_if->true_target->inbound_branches.Push(ir_if);
+    ir_if->false_target->inbound_branches.Push(ir_if);
+
     return ir_if;
 }
 
-Loop* Builder::CreateLoop(const ast::LoopStatement* stmt) {
+Loop* Builder::CreateLoop(const ast::Statement* stmt) {
     auto* ir_loop = ir.flow_nodes.Create<Loop>(stmt);
     ir_loop->start_target = CreateBlock();
     ir_loop->continuing_target = CreateBlock();
     ir_loop->merge_target = CreateBlock();
 
+    // A loop always branches to the start block.
+    ir_loop->start_target->inbound_branches.Push(ir_loop);
+
     return ir_loop;
 }
 
-void Builder::Branch(Block* from, const FlowNode* to) {
+Switch* Builder::CreateSwitch(const ast::SwitchStatement* stmt) {
+    auto* ir_switch = ir.flow_nodes.Create<Switch>(stmt);
+    ir_switch->merge_target = CreateBlock();
+    return ir_switch;
+}
+
+Block* Builder::CreateCase(Switch* s, const utils::VectorRef<const ast::CaseSelector*> selectors) {
+    s->cases.Push(Switch::Case{selectors, CreateBlock()});
+
+    Block* b = s->cases.Back().start_target;
+    // Switch branches into the case block
+    b->inbound_branches.Push(s);
+    return b;
+}
+
+void Builder::Branch(Block* from, FlowNode* to) {
     TINT_ASSERT(IR, from);
     TINT_ASSERT(IR, to);
     from->branch_target = to;
+    to->inbound_branches.Push(from);
 }
 
 }  // namespace tint::ir
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index 531bcce..cf0de70 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -26,6 +26,9 @@
 namespace tint {
 class Program;
 }  // namespace tint
+namespace tint::ast {
+class CaseSelector;
+}  // namespace tint::ast
 
 namespace tint::ir {
 
@@ -52,30 +55,31 @@
     /// @returns the flow node
     Function* CreateFunction(const ast::Function* func);
 
-    /// Flags used for creation of if flow nodes
-    enum class IfFlags {
-        /// Do not create a merge node, `merge_target` will be `nullptr`
-        kSkipMerge,
-        /// Create the `merge_target` block
-        kCreateMerge,
-    };
-
     /// Creates an if flow node for the given ast::IfStatement or ast::BreakIfStatement
     /// @param stmt the ast::IfStatement or ast::BreakIfStatement
-    /// @param flags the if creation flags. By default the merge block will not be created, pass
-    ///              IfFlags::kCreateMerge if creation is desired.
     /// @returns the flow node
-    If* CreateIf(const ast::Statement* stmt, IfFlags flags = IfFlags::kSkipMerge);
+    If* CreateIf(const ast::Statement* stmt);
 
-    /// Creates a loop flow node for the given ast::LoopStatement
-    /// @param stmt the ast::LoopStatement
+    /// Creates a loop flow node for the given ast loop, while or for statement
+    /// @param stmt the ast loop, while or for statement
     /// @returns the flow node
-    Loop* CreateLoop(const ast::LoopStatement* stmt);
+    Loop* CreateLoop(const ast::Statement* stmt);
+
+    /// Creates a switch flow node for the given ast::SwitchStatement
+    /// @param stmt the ast::SwitchStatment
+    /// @returns the flow node
+    Switch* CreateSwitch(const ast::SwitchStatement* stmt);
+
+    /// Creates a case flow node for the given case branch.
+    /// @param s the switch to create the case into
+    /// @param selectors the case selectors for the case statement
+    /// @returns the start block for the case flow node
+    Block* CreateCase(Switch* s, const utils::VectorRef<const ast::CaseSelector*> selectors);
 
     /// Branches the given block to the given flow node.
     /// @param from the block to branch from
     /// @param to the node to branch too
-    void Branch(Block* from, const FlowNode* to);
+    void Branch(Block* from, FlowNode* to);
 
     /// The IR module.
     Module ir;
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index 5f7553f..6bbcf6d 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -19,11 +19,16 @@
 #include "src/tint/ast/break_if_statement.h"
 #include "src/tint/ast/break_statement.h"
 #include "src/tint/ast/continue_statement.h"
+#include "src/tint/ast/fallthrough_statement.h"
+#include "src/tint/ast/for_loop_statement.h"
 #include "src/tint/ast/function.h"
 #include "src/tint/ast/if_statement.h"
+#include "src/tint/ast/loop_statement.h"
 #include "src/tint/ast/return_statement.h"
 #include "src/tint/ast/statement.h"
 #include "src/tint/ast/static_assert.h"
+#include "src/tint/ast/switch_statement.h"
+#include "src/tint/ast/while_statement.h"
 #include "src/tint/ir/function.h"
 #include "src/tint/ir/if.h"
 #include "src/tint/ir/loop.h"
@@ -50,23 +55,41 @@
     BuilderImpl* impl_;
 };
 
+bool IsBranched(const Block* b) {
+    return b->branch_target != nullptr;
+}
+
+bool IsConnected(const FlowNode* b) {
+    // Function is always connected as it's the start.
+    if (b->Is<ir::Function>()) {
+        return true;
+    }
+
+    for (auto* parent : b->inbound_branches) {
+        if (IsConnected(parent)) {
+            return true;
+        }
+    }
+    // Getting here means all the incoming branches are disconnected.
+    return false;
+}
+
 }  // namespace
 
 BuilderImpl::BuilderImpl(const Program* program) : builder_(program) {}
 
 BuilderImpl::~BuilderImpl() = default;
 
-void BuilderImpl::BranchTo(const FlowNode* node) {
+void BuilderImpl::BranchTo(FlowNode* node) {
     TINT_ASSERT(IR, current_flow_block_);
-    TINT_ASSERT(IR, !current_flow_block_->branch_target);
+    TINT_ASSERT(IR, !IsBranched(current_flow_block_));
 
     builder_.Branch(current_flow_block_, node);
-    current_flow_block_->branch_target = node;
     current_flow_block_ = nullptr;
 }
 
-void BuilderImpl::BranchToIfNeeded(const FlowNode* node) {
-    if (!current_flow_block_ || current_flow_block_->branch_target) {
+void BuilderImpl::BranchToIfNeeded(FlowNode* node) {
+    if (!current_flow_block_ || IsBranched(current_flow_block_)) {
         return;
     }
     BranchTo(node);
@@ -115,8 +138,10 @@
                 return true;
             },
             [&](Default) {
-                TINT_ICE(IR, diagnostics_) << "unhandled type: " << decl->TypeInfo().name;
-                return false;
+                diagnostics_.add_warning(tint::diag::System::IR,
+                                         "unknown type: " + std::string(decl->TypeInfo().name),
+                                         decl->source);
+                return true;
             });
         if (!ok) {
             return utils::Failure;
@@ -168,7 +193,7 @@
 
         // If the current flow block has a branch target then the rest of the statements in this
         // block are dead code. Skip them.
-        if (!current_flow_block_ || current_flow_block_->branch_target) {
+        if (!current_flow_block_ || IsBranched(current_flow_block_)) {
             break;
         }
     }
@@ -185,21 +210,22 @@
         //        [&](const ast::CallStatement* c) { },
         [&](const ast::ContinueStatement* c) { return EmitContinue(c); },
         //        [&](const ast::DiscardStatement* d) { },
-        //        [&](const ast::FallthroughStatement*) { },
+        [&](const ast::FallthroughStatement*) { return EmitFallthrough(); },
         [&](const ast::IfStatement* i) { return EmitIf(i); },
         [&](const ast::LoopStatement* l) { return EmitLoop(l); },
-        //        [&](const ast::ForLoopStatement* l) { },
-        //        [&](const ast::WhileStatement* 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) { },
+        [&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
         //        [&](const ast::VariableDeclStatement* v) { },
         [&](const ast::StaticAssert*) {
             return true;  // Not emitted
         },
         [&](Default) {
-            TINT_ICE(IR, diagnostics_)
-                << "unknown statement type: " << std::string(stmt->TypeInfo().name);
-            return false;
+            diagnostics_.add_warning(
+                tint::diag::System::IR,
+                "unknown statement type: " + std::string(stmt->TypeInfo().name), stmt->source);
+            return true;
         });
 }
 
@@ -228,31 +254,23 @@
         if (!EmitStatement(stmt->body)) {
             return false;
         }
+        // If the true branch did not execute control flow, then go to the merge target
+        BranchToIfNeeded(if_node->merge_target);
 
         current_flow_block_ = if_node->false_target;
         if (stmt->else_statement && !EmitStatement(stmt->else_statement)) {
             return false;
         }
+        // If the false branch did not execute control flow, then go to the merge target
+        BranchToIfNeeded(if_node->merge_target);
     }
     current_flow_block_ = nullptr;
 
     // If both branches went somewhere, then they both returned, continued or broke. So,
     // there is no need for the if merge-block and there is nothing to branch to the merge
     // block anyway.
-    if (if_node->true_target->branch_target && if_node->false_target->branch_target) {
-        return true;
-    }
-
-    if_node->merge_target = builder_.CreateBlock();
-    current_flow_block_ = if_node->merge_target;
-
-    // If the true branch did not execute control flow, then go to the merge target
-    if (!if_node->true_target->branch_target) {
-        if_node->true_target->branch_target = if_node->merge_target;
-    }
-    // If the false branch did not execute control flow, then go to the merge target
-    if (!if_node->false_target->branch_target) {
-        if_node->false_target->branch_target = if_node->merge_target;
+    if (IsConnected(if_node->merge_target)) {
+        current_flow_block_ = if_node->merge_target;
     }
 
     return true;
@@ -287,7 +305,143 @@
         BranchToIfNeeded(loop_node->start_target);
     }
 
+    // The loop merge can get disconnected if the loop returns directly, or the continuing target
+    // branches, eventually, to the merge, but nothing branched to the continuing target.
     current_flow_block_ = loop_node->merge_target;
+    if (!IsConnected(loop_node->merge_target)) {
+        current_flow_block_ = nullptr;
+    }
+    return true;
+}
+
+bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
+    auto* loop_node = builder_.CreateLoop(stmt);
+    // Continue is always empty, just go back to the start
+    builder_.Branch(loop_node->continuing_target, loop_node->start_target);
+
+    BranchTo(loop_node);
+
+    ast_to_flow_[stmt] = loop_node;
+
+    {
+        FlowStackScope scope(this, loop_node);
+
+        current_flow_block_ = loop_node->start_target;
+
+        // TODO(dsinclair): Emit the instructions for the condition
+
+        // Create an if (cond) {} else {break;} control flow
+        auto* if_node = builder_.CreateIf(nullptr);
+        builder_.Branch(if_node->true_target, if_node->merge_target);
+        builder_.Branch(if_node->false_target, loop_node->merge_target);
+        // TODO(dsinclair): set if condition register into if flow node
+
+        BranchTo(if_node);
+
+        current_flow_block_ = if_node->merge_target;
+        if (!EmitStatement(stmt->body)) {
+            return false;
+        }
+
+        BranchToIfNeeded(loop_node->continuing_target);
+    }
+    // The while loop always has a path to the merge target as the break statement comes before
+    // anything inside the loop.
+    current_flow_block_ = loop_node->merge_target;
+    return true;
+}
+
+bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
+    auto* loop_node = builder_.CreateLoop(stmt);
+    builder_.Branch(loop_node->continuing_target, loop_node->start_target);
+
+    if (stmt->initializer) {
+        // Emit the for initializer before branching to the loop
+        if (!EmitStatement(stmt->initializer)) {
+            return false;
+        }
+    }
+
+    BranchTo(loop_node);
+
+    ast_to_flow_[stmt] = loop_node;
+
+    {
+        FlowStackScope scope(this, loop_node);
+
+        current_flow_block_ = loop_node->start_target;
+
+        if (stmt->condition) {
+            // TODO(dsinclair): Emit the instructions for the condition
+
+            // Create an if (cond) {} else {break;} control flow
+            auto* if_node = builder_.CreateIf(nullptr);
+            builder_.Branch(if_node->true_target, if_node->merge_target);
+            builder_.Branch(if_node->false_target, loop_node->merge_target);
+            // TODO(dsinclair): set if condition register into if flow node
+
+            BranchTo(if_node);
+            current_flow_block_ = if_node->merge_target;
+        }
+
+        if (!EmitStatement(stmt->body)) {
+            return false;
+        }
+
+        BranchToIfNeeded(loop_node->continuing_target);
+
+        if (stmt->continuing) {
+            current_flow_block_ = loop_node->continuing_target;
+            if (!EmitStatement(stmt->continuing)) {
+                return false;
+            }
+        }
+    }
+    // The while loop always has a path to the merge target as the break statement comes before
+    // anything inside the loop.
+    current_flow_block_ = loop_node->merge_target;
+    return true;
+}
+
+bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
+    auto* switch_node = builder_.CreateSwitch(stmt);
+
+    // TODO(dsinclair): Emit the condition expression into the current block
+
+    BranchTo(switch_node);
+
+    ast_to_flow_[stmt] = switch_node;
+
+    {
+        FlowStackScope scope(this, switch_node);
+
+        // TODO(crbug.com/tint/1644): This can be simplifed when fallthrough is removed, a single
+        // loop can be used to iterate each body statement and emit for the case. Two loops are
+        // needed in order to have the target for a fallthrough.
+        for (const auto* c : stmt->body) {
+            builder_.CreateCase(switch_node, c->selectors);
+        }
+
+        for (size_t i = 0; i < stmt->body.Length(); ++i) {
+            current_flow_block_ = switch_node->cases[i].start_target;
+            if (i < (stmt->body.Length() - 1)) {
+                fallthrough_target_ = switch_node->cases[i + 1].start_target;
+            }
+
+            if (!EmitStatement(stmt->body[i]->body)) {
+                return false;
+            }
+            BranchToIfNeeded(switch_node->merge_target);
+
+            fallthrough_target_ = nullptr;
+        }
+    }
+    current_flow_block_ = nullptr;
+
+    if (IsConnected(switch_node->merge_target)) {
+        current_flow_block_ = switch_node->merge_target;
+    }
+
     return true;
 }
 
@@ -295,10 +449,6 @@
     // TODO(dsinclair): Emit the return value ....
 
     BranchTo(current_function_->end_target);
-
-    // TODO(dsinclair): The BranchTo will reset the current block. What happens with dead code
-    // after the return?
-
     return true;
 }
 
@@ -315,8 +465,6 @@
         return false;
     }
 
-    // TODO(dsinclair): The BranchTo will reset the current block. What happens with dead code
-    // after the break?
     return true;
 }
 
@@ -330,14 +478,11 @@
         TINT_UNREACHABLE(IR, diagnostics_);
     }
 
-    // TODO(dsinclair): The BranchTo will reset the current block. What happens with dead code
-    // after the continue?
-
     return true;
 }
 
 bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
-    auto* if_node = builder_.CreateIf(stmt, Builder::IfFlags::kCreateMerge);
+    auto* if_node = builder_.CreateIf(stmt);
 
     // TODO(dsinclair): Emit the condition expression into the current block
 
@@ -369,4 +514,10 @@
     return true;
 }
 
+bool BuilderImpl::EmitFallthrough() {
+    TINT_ASSERT(IR, fallthrough_target_ != nullptr);
+    BranchTo(fallthrough_target_);
+    return true;
+}
+
 }  // namespace tint::ir
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index 8daeebc..d6c4bc4 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -34,11 +34,13 @@
 class BreakIfStatement;
 class BreakStatement;
 class ContinueStatement;
+class ForLoopStatement;
 class Function;
 class IfStatement;
 class LoopStatement;
 class ReturnStatement;
 class Statement;
+class WhileStatement;
 }  // namespace tint::ast
 namespace tint::ir {
 class Block;
@@ -102,21 +104,40 @@
     /// @returns true if successful, false otherwise.
     bool EmitLoop(const ast::LoopStatement* stmt);
 
+    /// Emits a loop control node to the IR.
+    /// @param stmt the while statement
+    /// @returns true if successful, false otherwise.
+    bool EmitWhile(const ast::WhileStatement* stmt);
+
+    /// Emits a loop control node to the IR.
+    /// @param stmt the for loop statement
+    /// @returns true if successful, false otherwise.
+    bool EmitForLoop(const ast::ForLoopStatement* stmt);
+
+    /// Emits a switch statement
+    /// @param stmt the switch statement
+    /// @returns true if successful, false otherwise.
+    bool EmitSwitch(const ast::SwitchStatement* stmt);
+
     /// Emits a break statement
     /// @param stmt the break statement
-    /// @returns true if successfull, false otherwise.
+    /// @returns true if successful, false otherwise.
     bool EmitBreak(const ast::BreakStatement* stmt);
 
     /// Emits a continue statement
     /// @param stmt the continue statement
-    /// @returns true if successfull, false otherwise.
+    /// @returns true if successful, false otherwise.
     bool EmitContinue(const ast::ContinueStatement* stmt);
 
     /// Emits a break-if statement
     /// @param stmt the break-if statement
-    /// @returns true if successfull, false otherwise.
+    /// @returns true if successful, false otherwise.
     bool EmitBreakIf(const ast::BreakIfStatement* stmt);
 
+    /// Emits a fallthrough statement
+    /// @returns true if successful, false otherwise
+    bool EmitFallthrough();
+
     /// Retrieve the IR Flow node for a given AST node.
     /// @param n the node to lookup
     /// @returns the FlowNode for the given ast::Node or nullptr if it doesn't exist.
@@ -133,8 +154,8 @@
   private:
     enum class ControlFlags { kNone, kExcludeSwitch };
 
-    void BranchTo(const ir::FlowNode* node);
-    void BranchToIfNeeded(const ir::FlowNode* node);
+    void BranchTo(ir::FlowNode* node);
+    void BranchToIfNeeded(ir::FlowNode* node);
 
     FlowNode* FindEnclosingControl(ControlFlags flags);
 
@@ -145,6 +166,9 @@
     Block* current_flow_block_ = nullptr;
     Function* current_function_ = nullptr;
 
+    // TODO(crbug.com/tint/1644): Remove this when fallthrough is removed.
+    Block* fallthrough_target_ = nullptr;
+
     /// Map from ast nodes to flow nodes, used to retrieve the flow node for a given AST node.
     /// Used for testing purposes.
     std::unordered_map<const ast::Node*, const FlowNode*> ast_to_flow_;
diff --git a/src/tint/ir/builder_impl_test.cc b/src/tint/ir/builder_impl_test.cc
index fa37ac5..1ee66e5 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -14,9 +14,14 @@
 
 #include "src/tint/ir/test_helper.h"
 
+#include "src/tint/ast/case_selector.h"
+#include "src/tint/ast/int_literal_expression.h"
+
 namespace tint::ir {
 namespace {
 
+using namespace tint::number_suffixes;  // NOLINT
+
 using IRBuilderImplTest = TestHelper;
 
 TEST_F(IRBuilderImplTest, Func) {
@@ -36,6 +41,9 @@
     EXPECT_NE(f->start_target, nullptr);
     EXPECT_NE(f->end_target, nullptr);
 
+    EXPECT_EQ(1u, f->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, f->end_target->inbound_branches.Length());
+
     EXPECT_EQ(f->start_target->branch_target, f->end_target);
 }
 
@@ -82,6 +90,13 @@
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(func->start_target->branch_target, flow);
     EXPECT_EQ(flow->true_target->branch_target, flow->merge_target);
     EXPECT_EQ(flow->false_target->branch_target, flow->merge_target);
@@ -116,6 +131,13 @@
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(func->start_target->branch_target, flow);
     EXPECT_EQ(flow->true_target->branch_target, func->end_target);
     EXPECT_EQ(flow->false_target->branch_target, flow->merge_target);
@@ -150,6 +172,13 @@
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(func->start_target->branch_target, flow);
     EXPECT_EQ(flow->true_target->branch_target, flow->merge_target);
     EXPECT_EQ(flow->false_target->branch_target, func->end_target);
@@ -179,14 +208,78 @@
     auto* flow = ir_if->As<ir::If>();
     ASSERT_NE(flow->true_target, nullptr);
     ASSERT_NE(flow->false_target, nullptr);
+    ASSERT_NE(flow->merge_target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(func->start_target->branch_target, flow);
     EXPECT_EQ(flow->true_target->branch_target, func->end_target);
     EXPECT_EQ(flow->false_target->branch_target, func->end_target);
-    EXPECT_EQ(flow->merge_target, nullptr);
+}
+
+TEST_F(IRBuilderImplTest, IfStatement_JumpChainToMerge) {
+    // if (true) {
+    //   loop {
+    //     break;
+    //   }
+    // }
+    //
+    // func -> start -> if true
+    //               -> if false
+    //
+    //   [if true] -> loop
+    //   [if false] -> if merge
+    //   [if merge] -> func end
+    //   [loop] ->  loop start
+    //   [loop start] -> loop merge
+    //   [loop continuing] -> loop start
+    //   [loop merge] -> if merge
+    //
+    auto* ast_loop = Loop(Block(Break()));
+    auto* ast_if = If(true, Block(ast_loop));
+    WrapInFunction(ast_if);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_if = b.FlowNodeForAstNode(ast_if);
+    ASSERT_NE(ir_if, nullptr);
+    EXPECT_TRUE(ir_if->Is<ir::If>());
+
+    auto* if_flow = ir_if->As<ir::If>();
+    ASSERT_NE(if_flow->true_target, nullptr);
+    ASSERT_NE(if_flow->false_target, nullptr);
+    ASSERT_NE(if_flow->merge_target, nullptr);
+
+    auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+    ASSERT_NE(ir_loop, nullptr);
+    EXPECT_TRUE(ir_loop->Is<ir::Loop>());
+
+    auto* loop_flow = ir_loop->As<ir::Loop>();
+    ASSERT_NE(loop_flow->start_target, nullptr);
+    ASSERT_NE(loop_flow->continuing_target, nullptr);
+    ASSERT_NE(loop_flow->merge_target, nullptr);
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    EXPECT_EQ(func->start_target->branch_target, if_flow);
+    EXPECT_EQ(if_flow->true_target->branch_target, loop_flow);
+    EXPECT_EQ(loop_flow->start_target->branch_target, loop_flow->merge_target);
+    EXPECT_EQ(loop_flow->merge_target->branch_target, if_flow->merge_target);
+    EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
+    EXPECT_EQ(if_flow->false_target->branch_target, if_flow->merge_target);
+    EXPECT_EQ(if_flow->merge_target->branch_target, func->end_target);
 }
 
 TEST_F(IRBuilderImplTest, Loop_WithBreak) {
@@ -214,6 +307,13 @@
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(func->start_target->branch_target, flow);
     EXPECT_EQ(flow->start_target->branch_target, flow->merge_target);
     EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
@@ -260,6 +360,17 @@
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(func->start_target->branch_target, loop_flow);
     EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
     EXPECT_EQ(if_flow->true_target->branch_target, loop_flow->merge_target);
@@ -308,6 +419,17 @@
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, break_if_flow->inbound_branches.Length());
+    EXPECT_EQ(1u, break_if_flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, break_if_flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, break_if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(func->start_target->branch_target, loop_flow);
     EXPECT_EQ(loop_flow->start_target->branch_target, loop_flow->continuing_target);
     EXPECT_EQ(loop_flow->continuing_target->branch_target, break_if_flow);
@@ -325,7 +447,7 @@
     //   [if false] -> if merge
     //   [if merge] -> loop continuing
     //   [loop continuing] -> loop start
-    //   [loop merge] -> func end
+    //   [loop merge] -> nullptr
     //
     auto* ast_if = If(true, Block(Return()));
     auto* ast_loop = Loop(Block(ast_if, Continue()));
@@ -357,14 +479,147 @@
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
     EXPECT_EQ(if_flow->true_target->branch_target, func->end_target);
     EXPECT_EQ(if_flow->false_target->branch_target, if_flow->merge_target);
-
     EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
 
     EXPECT_EQ(func->start_target->branch_target, ir_loop);
-    EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(loop_flow->merge_target->branch_target, nullptr);
+}
+
+TEST_F(IRBuilderImplTest, Loop_WithOnlyReturn) {
+    // {
+    //   loop {
+    //     return;
+    //     continue;
+    //   }
+    //   if true { return; }
+    // }
+    //
+    // func -> start -> loop -> loop start -> return -> func end
+    //
+    //   [loop continuing] -> loop start
+    //   [loop merge] -> nullptr
+    //
+    // Note, the continue; is here is a dead call, so we won't emit a branch to the continuing block
+    // so the inbound_branches will be zero for continuing.
+    //
+    // The `if` after the `loop` is also eliminated as there is no control-flow path reaching the
+    // block.
+    auto* ast_loop = Loop(Block(Return(), Continue()));
+    WrapInFunction(ast_loop, If(true, Block(Return())));
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+    ASSERT_NE(ir_loop, nullptr);
+    EXPECT_TRUE(ir_loop->Is<ir::Loop>());
+
+    auto* loop_flow = ir_loop->As<ir::Loop>();
+    ASSERT_NE(loop_flow->start_target, nullptr);
+    ASSERT_NE(loop_flow->continuing_target, nullptr);
+    ASSERT_NE(loop_flow->merge_target, nullptr);
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
+    EXPECT_EQ(loop_flow->start_target->branch_target, func->end_target);
+    EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
+
+    EXPECT_EQ(func->start_target->branch_target, ir_loop);
+}
+
+TEST_F(IRBuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
+    // {
+    //   loop {
+    //     return;
+    //     continuing {
+    //       break if true;
+    //     }
+    //   }
+    //   if (true) { return; }
+    // }
+    //
+    // func -> start -> loop -> loop start -> return -> func end
+    //
+    //   [loop continuing] -> break if true
+    //                     -> break if false
+    //   [break if true] -> loop merge
+    //   [break if false] -> if merge
+    //   [break if merge] -> loop start
+    //   [loop merge] -> nullptr
+    //
+    // In this case, the continuing block is dead code, but we don't really know that when parsing
+    // so we end up with a branch into the loop merge target. The loop merge can tell it's dead code
+    // so we can drop the if ater the loop.
+    auto* ast_break_if = BreakIf(true);
+    auto* ast_loop = Loop(Block(Return()), Block(ast_break_if));
+    auto* ast_if = If(true, Block(Return()));
+    WrapInFunction(Block(ast_loop, ast_if));
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+    ASSERT_NE(ir_loop, nullptr);
+    EXPECT_TRUE(ir_loop->Is<ir::Loop>());
+
+    auto* loop_flow = ir_loop->As<ir::Loop>();
+    ASSERT_NE(loop_flow->start_target, nullptr);
+    ASSERT_NE(loop_flow->continuing_target, nullptr);
+    ASSERT_NE(loop_flow->merge_target, nullptr);
+
+    auto* ir_if = b.FlowNodeForAstNode(ast_if);
+    EXPECT_EQ(ir_if, nullptr);
+
+    auto* ir_break_if = b.FlowNodeForAstNode(ast_break_if);
+    ASSERT_NE(ir_break_if, nullptr);
+    EXPECT_TRUE(ir_break_if->Is<ir::If>());
+
+    auto* break_if_flow = ir_break_if->As<ir::If>();
+    ASSERT_NE(break_if_flow->true_target, nullptr);
+    ASSERT_NE(break_if_flow->false_target, nullptr);
+    ASSERT_NE(break_if_flow->merge_target, nullptr);
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    // This is 1 because only the loop branch happens. The subsequent if return is dead code.
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
+    EXPECT_EQ(loop_flow->start_target->branch_target, func->end_target);
+    EXPECT_EQ(loop_flow->continuing_target->branch_target, break_if_flow);
+
+    EXPECT_EQ(func->start_target->branch_target, ir_loop);
 }
 
 TEST_F(IRBuilderImplTest, Loop_WithIf_BothBranchesBreak) {
@@ -402,10 +657,22 @@
     auto* if_flow = ir_if->As<ir::If>();
     ASSERT_NE(if_flow->true_target, nullptr);
     ASSERT_NE(if_flow->false_target, nullptr);
+    ASSERT_NE(if_flow->merge_target, nullptr);
 
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(0u, if_flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
     // Note, the `continue` is dead code because both if branches go out of loop, so it just gets
     // dropped.
 
@@ -413,7 +680,6 @@
     EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
     EXPECT_EQ(if_flow->true_target->branch_target, loop_flow->merge_target);
     EXPECT_EQ(if_flow->false_target->branch_target, loop_flow->merge_target);
-    EXPECT_EQ(if_flow->merge_target, nullptr);
     EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
     EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
 }
@@ -551,6 +817,41 @@
     ASSERT_EQ(1u, m.functions.Length());
     auto* func = m.functions[0];
 
+    EXPECT_EQ(1u, loop_flow_a->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_a->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_a->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_a->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_b->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_b->start_target->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_b->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_b->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_c->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_c->start_target->inbound_branches.Length());
+    EXPECT_EQ(0u, loop_flow_c->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_c->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_d->inbound_branches.Length());
+    EXPECT_EQ(2u, loop_flow_d->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_d->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, loop_flow_d->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_a->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_a->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_a->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_a->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_b->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_b->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_b->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_b->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_c->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_c->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_c->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_c->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_d->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_d->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_d->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow_d->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
     EXPECT_EQ(func->start_target->branch_target, loop_flow_a);
     EXPECT_EQ(loop_flow_a->start_target->branch_target, loop_flow_b);
     EXPECT_EQ(loop_flow_b->start_target->branch_target, if_flow_a);
@@ -578,5 +879,498 @@
     EXPECT_EQ(loop_flow_a->merge_target->branch_target, func->end_target);
 }
 
+TEST_F(IRBuilderImplTest, While) {
+    // {
+    //   while false {
+    //   }
+    // }
+    //
+    // func -> while -> loop_start -> if true
+    //                             -> if false
+    //
+    //   [if true] -> if merge
+    //   [if false] -> while merge
+    //   [if merge] -> loop continuing
+    //   [loop continuing] -> loop start
+    //   [while merge] -> func end
+    //
+    auto* ast_while = While(false, Block());
+    WrapInFunction(ast_while);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_while = b.FlowNodeForAstNode(ast_while);
+    ASSERT_NE(ir_while, nullptr);
+    ASSERT_TRUE(ir_while->Is<ir::Loop>());
+
+    auto* flow = ir_while->As<ir::Loop>();
+    ASSERT_NE(flow->start_target, nullptr);
+    ASSERT_NE(flow->continuing_target, nullptr);
+    ASSERT_NE(flow->merge_target, nullptr);
+
+    ASSERT_NE(flow->start_target->branch_target, nullptr);
+    ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
+    auto* if_flow = flow->start_target->branch_target->As<ir::If>();
+    ASSERT_NE(if_flow->true_target, nullptr);
+    ASSERT_NE(if_flow->false_target, nullptr);
+    ASSERT_NE(if_flow->merge_target, nullptr);
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+
+    EXPECT_EQ(func->start_target->branch_target, flow);
+    EXPECT_EQ(flow->start_target->branch_target, if_flow);
+    EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
+    EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
+    EXPECT_EQ(if_flow->merge_target->branch_target, flow->continuing_target);
+    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
+    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+}
+
+TEST_F(IRBuilderImplTest, While_Return) {
+    // {
+    //   while true {
+    //     return;
+    //   }
+    // }
+    //
+    // func -> while -> if true
+    //                  if false
+    //
+    //   [if true] -> if merge
+    //   [if false] -> while merge
+    //   [if merge] -> func end
+    //   [while merge] -> func end
+    //
+    auto* ast_while = While(true, Block(Return()));
+    WrapInFunction(ast_while);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_while = b.FlowNodeForAstNode(ast_while);
+    ASSERT_NE(ir_while, nullptr);
+    ASSERT_TRUE(ir_while->Is<ir::Loop>());
+
+    auto* flow = ir_while->As<ir::Loop>();
+    ASSERT_NE(flow->start_target, nullptr);
+    ASSERT_NE(flow->continuing_target, nullptr);
+    ASSERT_NE(flow->merge_target, nullptr);
+
+    ASSERT_NE(flow->start_target->branch_target, nullptr);
+    ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
+    auto* if_flow = flow->start_target->branch_target->As<ir::If>();
+    ASSERT_NE(if_flow->true_target, nullptr);
+    ASSERT_NE(if_flow->false_target, nullptr);
+    ASSERT_NE(if_flow->merge_target, nullptr);
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+
+    EXPECT_EQ(func->start_target->branch_target, flow);
+    EXPECT_EQ(flow->start_target->branch_target, if_flow);
+    EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
+    EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
+    EXPECT_EQ(if_flow->merge_target->branch_target, func->end_target);
+    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
+    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+}
+
+// TODO(dsinclair): Enable when variable declarations and increment are supported
+TEST_F(IRBuilderImplTest, DISABLED_For) {
+    // for(var i: 0; i < 10; i++) {
+    // }
+    //
+    // func -> loop -> loop start -> if true
+    //                            -> if false
+    //
+    //   [if true] -> if merge
+    //   [if false] -> loop merge
+    //   [if merge] -> loop continuing
+    //   [loop continuing] -> loop start
+    //   [loop merge] -> func end
+    //
+    auto* ast_for = For(Decl(Var("i", ty.i32())), LessThan("i", 10_a), Increment("i"), Block());
+    WrapInFunction(ast_for);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_for = b.FlowNodeForAstNode(ast_for);
+    ASSERT_NE(ir_for, nullptr);
+    ASSERT_TRUE(ir_for->Is<ir::Loop>());
+
+    auto* flow = ir_for->As<ir::Loop>();
+    ASSERT_NE(flow->start_target, nullptr);
+    ASSERT_NE(flow->continuing_target, nullptr);
+    ASSERT_NE(flow->merge_target, nullptr);
+
+    ASSERT_NE(flow->start_target->branch_target, nullptr);
+    ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
+    auto* if_flow = flow->start_target->branch_target->As<ir::If>();
+    ASSERT_NE(if_flow->true_target, nullptr);
+    ASSERT_NE(if_flow->false_target, nullptr);
+    ASSERT_NE(if_flow->merge_target, nullptr);
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
+    EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+
+    EXPECT_EQ(func->start_target->branch_target, flow);
+    EXPECT_EQ(flow->start_target->branch_target, if_flow);
+    EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
+    EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
+    EXPECT_EQ(if_flow->merge_target->branch_target, flow->continuing_target);
+    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
+    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+}
+
+TEST_F(IRBuilderImplTest, For_NoInitCondOrContinuing) {
+    // for (;;) {
+    //   break;
+    // }
+    //
+    // func -> loop -> loop start -> loop merge -> func end
+    //
+    auto* ast_for = For(nullptr, nullptr, nullptr, Block(Break()));
+    WrapInFunction(ast_for);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_for = b.FlowNodeForAstNode(ast_for);
+    ASSERT_NE(ir_for, nullptr);
+    ASSERT_TRUE(ir_for->Is<ir::Loop>());
+
+    auto* flow = ir_for->As<ir::Loop>();
+    ASSERT_NE(flow->start_target, nullptr);
+    ASSERT_NE(flow->continuing_target, nullptr);
+    ASSERT_NE(flow->merge_target, nullptr);
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
+    EXPECT_EQ(flow->start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
+    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+}
+
+TEST_F(IRBuilderImplTest, Switch) {
+    // func -> switch -> case 1
+    //                -> case 2
+    //                -> default
+    //
+    //   [case 1] -> switch merge
+    //   [case 2] -> switch merge
+    //   [default] -> switch merge
+    //   [switch merge] -> func end
+    //
+    auto* ast_switch = Switch(
+        1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block()),
+                           Case(utils::Vector{CaseSelector(1_i)}, Block()), DefaultCase(Block())});
+
+    WrapInFunction(ast_switch);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+    ASSERT_NE(ir_switch, nullptr);
+    ASSERT_TRUE(ir_switch->Is<ir::Switch>());
+
+    auto* flow = ir_switch->As<ir::Switch>();
+    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_EQ(3u, flow->cases.Length());
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    ASSERT_EQ(1u, flow->cases[0].selectors.Length());
+    ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
+    EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+
+    ASSERT_EQ(1u, flow->cases[1].selectors.Length());
+    ASSERT_TRUE(flow->cases[1].selectors[0]->expr->Is<ast::IntLiteralExpression>());
+    EXPECT_EQ(1_i, flow->cases[1].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+
+    ASSERT_EQ(1u, flow->cases[2].selectors.Length());
+    EXPECT_TRUE(flow->cases[2].selectors[0]->IsDefault());
+
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[2].start_target->inbound_branches.Length());
+    EXPECT_EQ(3u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
+    EXPECT_EQ(func->start_target->branch_target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->cases[1].start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->cases[2].start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+}
+
+TEST_F(IRBuilderImplTest, Switch_OnlyDefault) {
+    // func -> switch -> default -> switch merge -> func end
+    //
+    auto* ast_switch = Switch(1_i, utils::Vector{DefaultCase(Block())});
+
+    WrapInFunction(ast_switch);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+    ASSERT_NE(ir_switch, nullptr);
+    ASSERT_TRUE(ir_switch->Is<ir::Switch>());
+
+    auto* flow = ir_switch->As<ir::Switch>();
+    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_EQ(1u, flow->cases.Length());
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    ASSERT_EQ(1u, flow->cases[0].selectors.Length());
+    EXPECT_TRUE(flow->cases[0].selectors[0]->IsDefault());
+
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
+    EXPECT_EQ(func->start_target->branch_target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+}
+
+TEST_F(IRBuilderImplTest, Switch_WithBreak) {
+    // {
+    //   switch(1) {
+    //     case 0: {
+    //       break;
+    //       if true { return;}   // Dead code
+    //     }
+    //     default: {}
+    //   }
+    // }
+    //
+    // func -> switch -> case 1
+    //                -> default
+    //
+    //   [case 1] -> switch merge
+    //   [default] -> switch merge
+    //   [switch merge] -> func end
+    auto* ast_switch = Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)},
+                                                      Block(Break(), If(true, Block(Return())))),
+                                                 DefaultCase(Block())});
+
+    WrapInFunction(ast_switch);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+    ASSERT_NE(ir_switch, nullptr);
+    ASSERT_TRUE(ir_switch->Is<ir::Switch>());
+
+    auto* flow = ir_switch->As<ir::Switch>();
+    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_EQ(2u, flow->cases.Length());
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    ASSERT_EQ(1u, flow->cases[0].selectors.Length());
+    ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
+    EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+
+    ASSERT_EQ(1u, flow->cases[1].selectors.Length());
+    EXPECT_TRUE(flow->cases[1].selectors[0]->IsDefault());
+
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->merge_target->inbound_branches.Length());
+    // This is 1 because the if is dead-code eliminated and the return doesn't happen.
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
+    EXPECT_EQ(func->start_target->branch_target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->cases[1].start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+}
+
+TEST_F(IRBuilderImplTest, Switch_AllReturn) {
+    // {
+    //   switch(1) {
+    //     case 0: {
+    //       return;
+    //     }
+    //     default: {
+    //       return;
+    //     }
+    //   }
+    //   if true { return; }  // Dead code
+    // }
+    //
+    // func -> switch -> case 1
+    //                -> default
+    //
+    //   [case 1] -> func end
+    //   [default] -> func end
+    //   [switch merge] -> nullptr
+    //
+    auto* ast_switch =
+        Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block(Return())),
+                                  DefaultCase(Block(Return()))});
+
+    auto* ast_if = If(true, Block(Return()));
+
+    WrapInFunction(ast_switch, ast_if);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    ASSERT_EQ(b.FlowNodeForAstNode(ast_if), nullptr);
+
+    auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+    ASSERT_NE(ir_switch, nullptr);
+    ASSERT_TRUE(ir_switch->Is<ir::Switch>());
+
+    auto* flow = ir_switch->As<ir::Switch>();
+    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_EQ(2u, flow->cases.Length());
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    ASSERT_EQ(1u, flow->cases[0].selectors.Length());
+    ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
+    EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+
+    ASSERT_EQ(1u, flow->cases[1].selectors.Length());
+    EXPECT_TRUE(flow->cases[1].selectors[0]->IsDefault());
+
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
+    EXPECT_EQ(0u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
+
+    EXPECT_EQ(func->start_target->branch_target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start_target->branch_target, func->end_target);
+    EXPECT_EQ(flow->cases[1].start_target->branch_target, func->end_target);
+    EXPECT_EQ(flow->merge_target->branch_target, nullptr);
+}
+
+// TODO(crbug.com/tint/1644): Remove when fallthrough is removed.
+TEST_F(IRBuilderImplTest, Switch_Fallthrough) {
+    // func -> switch -> case 1
+    //                -> case 2
+    //                -> default
+    //
+    //   [case 1] -> switch merge
+    //   [case 2] -> default
+    //   [default] -> switch merge
+    //   [switch merge] -> func end
+    //
+    auto* ast_switch =
+        Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block()),
+                                  Case(utils::Vector{CaseSelector(1_i)}, Block(Fallthrough())),
+                                  DefaultCase(Block())});
+
+    WrapInFunction(ast_switch);
+    auto& b = Build();
+
+    auto r = b.Build();
+    ASSERT_TRUE(r) << b.error();
+    auto m = r.Move();
+
+    auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+    ASSERT_NE(ir_switch, nullptr);
+    ASSERT_TRUE(ir_switch->Is<ir::Switch>());
+
+    auto* flow = ir_switch->As<ir::Switch>();
+    ASSERT_NE(flow->merge_target, nullptr);
+    ASSERT_EQ(3u, flow->cases.Length());
+
+    ASSERT_EQ(1u, m.functions.Length());
+    auto* func = m.functions[0];
+
+    ASSERT_EQ(1u, flow->cases[0].selectors.Length());
+    ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
+    EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+
+    ASSERT_EQ(1u, flow->cases[1].selectors.Length());
+    ASSERT_TRUE(flow->cases[1].selectors[0]->expr->Is<ast::IntLiteralExpression>());
+    EXPECT_EQ(1_i, flow->cases[1].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+
+    ASSERT_EQ(1u, flow->cases[2].selectors.Length());
+    EXPECT_TRUE(flow->cases[2].selectors[0]->IsDefault());
+
+    EXPECT_EQ(1u, flow->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
+    EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->cases[2].start_target->inbound_branches.Length());
+    EXPECT_EQ(2u, flow->merge_target->inbound_branches.Length());
+    EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
+
+    EXPECT_EQ(func->start_target->branch_target, ir_switch);
+    EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->cases[1].start_target->branch_target, flow->cases[2].start_target);
+    EXPECT_EQ(flow->cases[2].start_target->branch_target, flow->merge_target);
+    EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+}
+
 }  // namespace
 }  // namespace tint::ir
diff --git a/src/tint/ir/debug.cc b/src/tint/ir/debug.cc
new file mode 100644
index 0000000..be83111
--- /dev/null
+++ b/src/tint/ir/debug.cc
@@ -0,0 +1,159 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ir/debug.h"
+
+#include <sstream>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "src/tint/ir/block.h"
+#include "src/tint/ir/if.h"
+#include "src/tint/ir/loop.h"
+#include "src/tint/ir/switch.h"
+#include "src/tint/ir/terminator.h"
+#include "src/tint/program.h"
+
+namespace tint::ir {
+
+// static
+std::string Debug::AsDotGraph(const Module* mod) {
+    size_t node_count = 0;
+
+    std::unordered_set<const FlowNode*> visited;
+    std::unordered_set<const FlowNode*> merge_nodes;
+    std::unordered_map<const FlowNode*, std::string> node_to_name;
+    std::stringstream out;
+
+    auto name_for = [&](const FlowNode* node) -> std::string {
+        if (node_to_name.count(node) > 0) {
+            return node_to_name[node];
+        }
+
+        std::string name = "node_" + std::to_string(node_count);
+        node_count += 1;
+
+        node_to_name[node] = name;
+        return name;
+    };
+
+    std::function<void(const FlowNode*)> Graph = [&](const FlowNode* node) {
+        if (visited.count(node) > 0) {
+            return;
+        }
+        visited.insert(node);
+
+        tint::Switch(
+            node,
+            [&](const ir::Block* b) {
+                if (node_to_name.count(b) == 0) {
+                    out << name_for(b) << R"( [label="block"])" << std::endl;
+                }
+                out << name_for(b) << " -> " << name_for(b->branch_target);
+
+                // Dashed lines to merge blocks
+                if (merge_nodes.count(b->branch_target) != 0) {
+                    out << " [style=dashed]";
+                }
+
+                out << std::endl;
+                Graph(b->branch_target);
+            },
+            [&](const ir::Switch* s) {
+                out << name_for(s) << R"( [label="switch"])" << std::endl;
+                out << name_for(s->merge_target) << R"( [label="switch merge"])" << std::endl;
+                merge_nodes.insert(s->merge_target);
+
+                size_t i = 0;
+                for (const auto& c : s->cases) {
+                    out << name_for(c.start_target)
+                        << R"( [label="case )" + std::to_string(i++) + R"("])" << std::endl;
+                }
+                out << name_for(s) << " -> {";
+                for (const auto& c : s->cases) {
+                    if (&c != &(s->cases[0])) {
+                        out << ", ";
+                    }
+                    out << name_for(c.start_target);
+                }
+                out << "}" << std::endl;
+
+                for (const auto& c : s->cases) {
+                    Graph(c.start_target);
+                }
+                Graph(s->merge_target);
+            },
+            [&](const ir::If* i) {
+                out << name_for(i) << R"( [label="if"])" << std::endl;
+                out << name_for(i->true_target) << R"( [label="true"])" << std::endl;
+                out << name_for(i->false_target) << R"( [label="false"])" << std::endl;
+                out << name_for(i->merge_target) << R"( [label="if merge"])" << std::endl;
+                merge_nodes.insert(i->merge_target);
+
+                out << name_for(i) << " -> {";
+                out << name_for(i->true_target) << ", " << name_for(i->false_target);
+                out << "}" << std::endl;
+
+                // Subgraph if true/false branches so they draw on the same line
+                out << "subgraph sub_" << name_for(i) << " {" << std::endl;
+                out << R"(rank="same")" << std::endl;
+                out << name_for(i->true_target) << std::endl;
+                out << name_for(i->false_target) << std::endl;
+                out << "}" << std::endl;
+
+                Graph(i->true_target);
+                Graph(i->false_target);
+                Graph(i->merge_target);
+            },
+            [&](const ir::Loop* l) {
+                out << name_for(l) << R"( [label="loop"])" << std::endl;
+                out << name_for(l->start_target) << R"( [label="start"])" << std::endl;
+                out << name_for(l->continuing_target) << R"( [label="continuing"])" << std::endl;
+                out << name_for(l->merge_target) << R"( [label="loop merge"])" << std::endl;
+                merge_nodes.insert(l->merge_target);
+
+                // Subgraph the continuing and merge so they get drawn on the same line
+                out << "subgraph sub_" << name_for(l) << " {" << std::endl;
+                out << R"(rank="same")" << std::endl;
+                out << name_for(l->continuing_target) << std::endl;
+                out << name_for(l->merge_target) << std::endl;
+                out << "}" << std::endl;
+
+                out << name_for(l) << " -> " << name_for(l->start_target) << std::endl;
+
+                Graph(l->start_target);
+                Graph(l->continuing_target);
+                Graph(l->merge_target);
+            },
+            [&](const ir::Terminator*) {
+                // Already done
+            });
+    };
+
+    out << "digraph G {" << std::endl;
+    for (const auto* func : mod->functions) {
+        // Cluster each function to label and draw a box around it.
+        out << "subgraph cluster_" << name_for(func) << " {" << std::endl;
+        out << R"(label=")" << mod->program->Symbols().NameFor(func->source->symbol) << R"(")"
+            << std::endl;
+        out << name_for(func->start_target) << R"( [label="start"])" << std::endl;
+        out << name_for(func->end_target) << R"( [label="end"])" << std::endl;
+        Graph(func->start_target);
+        out << "}" << std::endl;
+    }
+    out << "}";
+    return out.str();
+}
+
+}  // namespace tint::ir
diff --git a/src/tint/ir/debug.h b/src/tint/ir/debug.h
new file mode 100644
index 0000000..bd0570b
--- /dev/null
+++ b/src/tint/ir/debug.h
@@ -0,0 +1,35 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_IR_DEBUG_H_
+#define SRC_TINT_IR_DEBUG_H_
+
+#include <string>
+
+#include "src/tint/ir/module.h"
+
+namespace tint::ir {
+
+/// Helper class to debug IR.
+class Debug {
+  public:
+    /// Returns the module as a dot graph
+    /// @param mod the module to emit
+    /// @returns the dot graph for the given module
+    static std::string AsDotGraph(const Module* mod);
+};
+
+}  // namespace tint::ir
+
+#endif  // SRC_TINT_IR_DEBUG_H_
diff --git a/src/tint/ir/flow_node.h b/src/tint/ir/flow_node.h
index 0158af4..2b5ec39 100644
--- a/src/tint/ir/flow_node.h
+++ b/src/tint/ir/flow_node.h
@@ -16,6 +16,7 @@
 #define SRC_TINT_IR_FLOW_NODE_H_
 
 #include "src/tint/castable.h"
+#include "src/tint/utils/vector.h"
 
 namespace tint::ir {
 
@@ -24,6 +25,13 @@
   public:
     ~FlowNode() override;
 
+    /// The list of flow nodes which branch into this node. This list maybe empty for several
+    /// reasons:
+    ///   - Node is a start node
+    ///   - Node is a merge target outside control flow (if that returns in both branches)
+    ///   - Node is a continue target outside control flow (loop that returns)
+    utils::Vector<FlowNode*, 2> inbound_branches;
+
   protected:
     /// Constructor
     FlowNode();
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 84967b0..2d28aa1 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -25,8 +25,7 @@
 
 namespace tint::ir {
 
-/// A flow node representing an if statement. The node always contains a true and a false block. It
-/// may contain a merge block where the true/false blocks will merge too unless they return.
+/// A flow node representing an if statement.
 class If : public Castable<If, FlowNode> {
   public:
     /// Constructor
@@ -41,7 +40,8 @@
     Block* true_target = nullptr;
     /// The false branch block
     Block* false_target = nullptr;
-    /// An optional block where the true/false blocks will branch too if needed.
+    /// An block to reconvert the true/false barnches. The block always exists, but there maybe no
+    /// branches into it. (e.g. if both branches `return`)
     Block* merge_target = nullptr;
 };
 
diff --git a/src/tint/ir/loop.cc b/src/tint/ir/loop.cc
index 4458de6..46ac5a1 100644
--- a/src/tint/ir/loop.cc
+++ b/src/tint/ir/loop.cc
@@ -14,11 +14,17 @@
 
 #include "src/tint/ir/loop.h"
 
+#include "src/tint/ast/for_loop_statement.h"
+#include "src/tint/ast/loop_statement.h"
+#include "src/tint/ast/while_statement.h"
+
 TINT_INSTANTIATE_TYPEINFO(tint::ir::Loop);
 
 namespace tint::ir {
 
-Loop::Loop(const ast::LoopStatement* stmt) : Base(), source(stmt) {}
+Loop::Loop(const ast::Statement* s) : Base(), source(s) {
+    TINT_ASSERT(IR, (s->IsAnyOf<ast::LoopStatement, ast::WhileStatement, ast::ForLoopStatement>()));
+}
 
 Loop::~Loop() = default;
 
diff --git a/src/tint/ir/loop.h b/src/tint/ir/loop.h
index 03a0190..52d24fb 100644
--- a/src/tint/ir/loop.h
+++ b/src/tint/ir/loop.h
@@ -15,7 +15,7 @@
 #ifndef SRC_TINT_IR_LOOP_H_
 #define SRC_TINT_IR_LOOP_H_
 
-#include "src/tint/ast/loop_statement.h"
+#include "src/tint/ast/statement.h"
 #include "src/tint/ir/block.h"
 #include "src/tint/ir/flow_node.h"
 
@@ -25,12 +25,12 @@
 class Loop : public Castable<Loop, FlowNode> {
   public:
     /// Constructor
-    /// @param stmt the ast::LoopStatement
-    explicit Loop(const ast::LoopStatement* stmt);
+    /// @param stmt the loop, while or for statement.
+    explicit Loop(const ast::Statement* stmt);
     ~Loop() override;
 
-    /// The ast loop statement this ir loop is created from.
-    const ast::LoopStatement* source;
+    /// The ast loop, while or for statement this ir loop is created from.
+    const ast::Statement* source;
 
     /// The start block is the first block in a loop.
     Block* start_target = nullptr;
diff --git a/src/tint/ir/switch.cc b/src/tint/ir/switch.cc
index 9ad6d30..23b7fbb 100644
--- a/src/tint/ir/switch.cc
+++ b/src/tint/ir/switch.cc
@@ -18,7 +18,7 @@
 
 namespace tint::ir {
 
-Switch::Switch() : Base() {}
+Switch::Switch(const ast::SwitchStatement* stmt) : Base(), source(stmt) {}
 
 Switch::~Switch() = default;
 
diff --git a/src/tint/ir/switch.h b/src/tint/ir/switch.h
index e9de3ae..39d3d06 100644
--- a/src/tint/ir/switch.h
+++ b/src/tint/ir/switch.h
@@ -18,17 +18,38 @@
 #include "src/tint/ir/block.h"
 #include "src/tint/ir/flow_node.h"
 
+// Forward declarations
+namespace tint::ast {
+class CaseSelector;
+class SwitchStatement;
+}  // namespace tint::ast
+
 namespace tint::ir {
 
 /// Flow node representing a switch statement
 class Switch : public Castable<Switch, FlowNode> {
   public:
+    /// A case label in the struct
+    struct Case {
+        /// The case selector for this node
+        const utils::VectorRef<const ast::CaseSelector*> selectors;
+        /// The start block for the case block.
+        Block* start_target;
+    };
+
     /// Constructor
-    Switch();
+    /// @param stmt the originating ast switch statement
+    explicit Switch(const ast::SwitchStatement* stmt);
     ~Switch() override;
 
+    /// The originating switch statment in the AST
+    const ast::SwitchStatement* source;
+
     /// The switch merge target
     Block* merge_target;
+
+    /// The switch case statements
+    utils::Vector<Case, 4> cases;
 };
 
 }  // namespace tint::ir
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 25e6214..2f5b5e0 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -109,7 +109,7 @@
 #include "src/tint/sem/vector.h"
 #include "src/tint/sem/void.h"
 
-#ifdef INCLUDE_TINT_TINT_H_
+#ifdef CURRENTLY_IN_TINT_PUBLIC_HEADER
 #error "internal tint header being #included from tint.h"
 #endif
 
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 4c71607..d3febf2 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -3419,7 +3419,7 @@
         utils::Hashmap<uint32_t, Symbol, 8> copied_phis;
         for (const auto assignment : worklist) {
             const auto phi_id = assignment.phi_id;
-            if (read_set.Find(phi_id)) {
+            if (read_set.Contains(phi_id)) {
                 auto copy_name = namer_.MakeDerivedName(namer_.Name(phi_id) + "_c" +
                                                         std::to_string(block_info.id));
                 auto copy_sym = builder_.Symbols().Register(copy_name);
diff --git a/src/tint/reader/spirv/function_conversion_test.cc b/src/tint/reader/spirv/function_conversion_test.cc
index eab0031..1b28190 100644
--- a/src/tint/reader/spirv/function_conversion_test.cc
+++ b/src/tint/reader/spirv/function_conversion_test.cc
@@ -615,7 +615,6 @@
 // TODO(dneto): OpSConvert // only if multiple widths
 // TODO(dneto): OpUConvert // only if multiple widths
 // TODO(dneto): OpFConvert // only if multiple widths
-// TODO(dneto): OpQuantizeToF16 // only if f16 supported
 // TODO(dneto): OpSatConvertSToU // Kernel (OpenCL), not in WebGPU
 // TODO(dneto): OpSatConvertUToS // Kernel (OpenCL), not in WebGPU
 
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 62352ee..976706a 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -154,21 +154,26 @@
     /// Constructor that starts with the input `start` Source
     /// @param parser the parser
     /// @param start the start source of the range
-    MultiTokenSource(ParserImpl* parser, const Source& start) : parser_(parser), start_(start) {}
+    MultiTokenSource(ParserImpl* parser, const tint::Source& start)
+        : parser_(parser), start_(start) {}
 
-    /// Implicit conversion to Source that returns the combined source from start
-    /// to the current last token's source.
-    operator Source() const {
-        Source end = parser_->last_source().End();
+    /// @returns the Source that returns the combined source from start to the current last token's
+    /// source.
+    tint::Source Source() const {
+        auto end = parser_->last_source().End();
         if (end < start_) {
             end = start_;
         }
         return Source::Combine(start_, end);
     }
 
+    /// Implicit conversion to Source that returns the combined source from start to the current
+    /// last token's source.
+    operator tint::Source() const { return Source(); }
+
   private:
     ParserImpl* parser_;
-    Source start_;
+    tint::Source start_;
 };
 
 ParserImpl::TypedIdentifier::TypedIdentifier() = default;
@@ -1901,12 +1906,15 @@
 //   | LET optionally_typed_ident EQUAL expression
 //   | CONST optionally_typed_ident EQUAL expression
 Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_statement() {
+    auto decl_source_range = make_source_range();
     if (match(Token::Type::kConst)) {
-        auto decl = expect_optionally_typed_ident("'const' declaration");
-        if (decl.errored) {
+        auto typed_ident = expect_optionally_typed_ident("'const' declaration");
+        if (typed_ident.errored) {
             return Failure::kErrored;
         }
 
+        auto decl_source = decl_source_range.Source();
+
         if (!expect("'const' declaration", Token::Type::kEqual)) {
             return Failure::kErrored;
         }
@@ -1919,21 +1927,23 @@
             return add_error(peek(), "missing initializer for 'const' declaration");
         }
 
-        auto* const_ = create<ast::Const>(decl->source,                             // source
-                                          builder_.Symbols().Register(decl->name),  // symbol
-                                          decl->type,                               // type
-                                          initializer.value,                        // initializer
-                                          utils::Empty);                            // attributes
+        auto* const_ = create<ast::Const>(typed_ident->source,                             // source
+                                          builder_.Symbols().Register(typed_ident->name),  // symbol
+                                          typed_ident->type,                               // type
+                                          initializer.value,  // initializer
+                                          utils::Empty);      // attributes
 
-        return create<ast::VariableDeclStatement>(decl->source, const_);
+        return create<ast::VariableDeclStatement>(decl_source, const_);
     }
 
     if (match(Token::Type::kLet)) {
-        auto decl = expect_optionally_typed_ident("'let' declaration");
-        if (decl.errored) {
+        auto typed_ident = expect_optionally_typed_ident("'let' declaration");
+        if (typed_ident.errored) {
             return Failure::kErrored;
         }
 
+        auto decl_source = decl_source_range.Source();
+
         if (!expect("'let' declaration", Token::Type::kEqual)) {
             return Failure::kErrored;
         }
@@ -1946,13 +1956,13 @@
             return add_error(peek(), "missing initializer for 'let' declaration");
         }
 
-        auto* let = create<ast::Let>(decl->source,                             // source
-                                     builder_.Symbols().Register(decl->name),  // symbol
-                                     decl->type,                               // type
-                                     initializer.value,                        // initializer
-                                     utils::Empty);                            // attributes
+        auto* let = create<ast::Let>(typed_ident->source,                             // source
+                                     builder_.Symbols().Register(typed_ident->name),  // symbol
+                                     typed_ident->type,                               // type
+                                     initializer.value,                               // initializer
+                                     utils::Empty);                                   // attributes
 
-        return create<ast::VariableDeclStatement>(decl->source, let);
+        return create<ast::VariableDeclStatement>(decl_source, let);
     }
 
     auto decl = variable_decl();
@@ -1963,6 +1973,8 @@
         return Failure::kNoMatch;
     }
 
+    auto decl_source = decl_source_range.Source();
+
     const ast::Expression* initializer = nullptr;
     if (match(Token::Type::kEqual)) {
         auto initializer_expr = expression();
@@ -1976,7 +1988,7 @@
         initializer = initializer_expr.value;
     }
 
-    auto* var = create<ast::Var>(decl->source,                             // source
+    auto* var = create<ast::Var>(decl_source,                              // source
                                  builder_.Symbols().Register(decl->name),  // symbol
                                  decl->type,                               // type
                                  decl->address_space,                      // address space
diff --git a/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
index 703eb57..05f2976 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
@@ -28,10 +28,10 @@
     ASSERT_NE(e->variable, nullptr);
     EXPECT_EQ(e->variable->symbol, p->builder().Symbols().Get("a"));
 
-    ASSERT_EQ(e->source.range.begin.line, 1u);
-    ASSERT_EQ(e->source.range.begin.column, 5u);
-    ASSERT_EQ(e->source.range.end.line, 1u);
-    ASSERT_EQ(e->source.range.end.column, 6u);
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 1u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 12u);
 
     EXPECT_EQ(e->variable->initializer, nullptr);
 }
@@ -47,10 +47,10 @@
     ASSERT_NE(e->variable, nullptr);
     EXPECT_EQ(e->variable->symbol, p->builder().Symbols().Get("a"));
 
-    ASSERT_EQ(e->source.range.begin.line, 1u);
-    ASSERT_EQ(e->source.range.begin.column, 5u);
-    ASSERT_EQ(e->source.range.end.line, 1u);
-    ASSERT_EQ(e->source.range.end.column, 6u);
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 1u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 12u);
 
     ASSERT_NE(e->variable->initializer, nullptr);
     EXPECT_TRUE(e->variable->initializer->Is<ast::LiteralExpression>());
@@ -147,10 +147,10 @@
     ASSERT_NE(e.value, nullptr);
     ASSERT_TRUE(e->Is<ast::VariableDeclStatement>());
 
-    ASSERT_EQ(e->source.range.begin.line, 1u);
-    ASSERT_EQ(e->source.range.begin.column, 5u);
-    ASSERT_EQ(e->source.range.end.line, 1u);
-    ASSERT_EQ(e->source.range.end.column, 6u);
+    EXPECT_EQ(e->source.range.begin.line, 1u);
+    EXPECT_EQ(e->source.range.begin.column, 1u);
+    EXPECT_EQ(e->source.range.end.line, 1u);
+    EXPECT_EQ(e->source.range.end.column, 12u);
 }
 
 TEST_F(ParserImplTest, VariableStmt_Let_ComplexExpression) {
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 86d6825..762eea9 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -1606,12 +1606,22 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(GroupAndBindingTest, Binding_NonConstant) {
+    GlobalVar("val", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              Binding(Construct(ty.u32(), Call(Source{{12, 34}}, "dpdx", 1_a))), Group(1_i));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: @binding requires a const-expression, but expression is a runtime-expression)");
+}
+
 TEST_F(GroupAndBindingTest, Binding_Negative) {
     GlobalVar("val", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
               Binding(Source{{12, 34}}, -2_i), Group(1_i));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'binding' value must be non-negative)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @binding value must be non-negative)");
 }
 
 TEST_F(GroupAndBindingTest, Binding_F32) {
@@ -1619,7 +1629,7 @@
               Binding(Source{{12, 34}}, 2.0_f), Group(1_u));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'binding' must be an i32 or u32 value)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @binding must be an i32 or u32 value)");
 }
 
 TEST_F(GroupAndBindingTest, Binding_AFloat) {
@@ -1627,7 +1637,17 @@
               Binding(Source{{12, 34}}, 2.0_a), Group(1_u));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'binding' must be an i32 or u32 value)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @binding must be an i32 or u32 value)");
+}
+
+TEST_F(GroupAndBindingTest, Group_NonConstant) {
+    GlobalVar("val", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), Binding(2_u),
+              Group(Construct(ty.u32(), Call(Source{{12, 34}}, "dpdx", 1_a))));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: @group requires a const-expression, but expression is a runtime-expression)");
 }
 
 TEST_F(GroupAndBindingTest, Group_Negative) {
@@ -1635,7 +1655,7 @@
               Group(Source{{12, 34}}, -1_i));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'group' value must be non-negative)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @group value must be non-negative)");
 }
 
 TEST_F(GroupAndBindingTest, Group_F32) {
@@ -1643,7 +1663,7 @@
               Group(Source{{12, 34}}, 1.0_f));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'group' must be an i32 or u32 value)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @group must be an i32 or u32 value)");
 }
 
 TEST_F(GroupAndBindingTest, Group_AFloat) {
@@ -1651,7 +1671,7 @@
               Group(Source{{12, 34}}, 1.0_a));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'group' must be an i32 or u32 value)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @group must be an i32 or u32 value)");
 }
 
 using IdTest = ResolverTest;
@@ -1671,24 +1691,125 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(IdTest, NonConstant) {
+    Override("val", ty.f32(),
+             utils::Vector{Id(Construct(ty.u32(), Call(Source{{12, 34}}, "dpdx", 1_a)))});
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: @id requires a const-expression, but expression is a runtime-expression)");
+}
+
 TEST_F(IdTest, Negative) {
     Override("val", ty.f32(), utils::Vector{Id(Source{{12, 34}}, -1_i)});
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'id' value must be non-negative)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @id value must be non-negative)");
 }
 
 TEST_F(IdTest, F32) {
     Override("val", ty.f32(), utils::Vector{Id(Source{{12, 34}}, 1_f)});
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'id' must be an i32 or u32 value)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @id must be an i32 or u32 value)");
 }
 
 TEST_F(IdTest, AFloat) {
     Override("val", ty.f32(), utils::Vector{Id(Source{{12, 34}}, 1.0_a)});
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), R"(12:34 error: 'id' must be an i32 or u32 value)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: @id must be an i32 or u32 value)");
 }
 
+enum class LocationAttributeType {
+    kEntryPointParameter,
+    kEntryPointReturnType,
+    kStructureMember,
+};
+
+struct LocationTest : ResolverTestWithParam<LocationAttributeType> {
+    void Build(const ast::Expression* location_value) {
+        switch (GetParam()) {
+            case LocationAttributeType::kEntryPointParameter:
+                Func("main",
+                     utils::Vector{Param(Source{{12, 34}}, "a", ty.i32(),
+                                         utils::Vector{
+                                             Location(Source{{12, 34}}, location_value),
+                                             Flat(),
+                                         })},
+                     ty.void_(), utils::Empty,
+                     utils::Vector{
+                         Stage(ast::PipelineStage::kFragment),
+                     });
+                return;
+            case LocationAttributeType::kEntryPointReturnType:
+                Func("main", utils::Empty, ty.f32(),
+                     utils::Vector{
+                         Return(1_a),
+                     },
+                     utils::Vector{
+                         Stage(ast::PipelineStage::kFragment),
+                     },
+                     utils::Vector{
+                         Location(Source{{12, 34}}, location_value),
+                     });
+                return;
+            case LocationAttributeType::kStructureMember:
+                Structure("S", utils::Vector{
+                                   Member("m", ty.f32(),
+                                          utils::Vector{
+                                              Location(Source{{12, 34}}, location_value),
+                                          }),
+                               });
+                return;
+        }
+    }
+};
+
+TEST_P(LocationTest, Const_I32) {
+    Build(Expr(0_i));
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_P(LocationTest, Const_U32) {
+    Build(Expr(0_u));
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_P(LocationTest, Const_AInt) {
+    Build(Expr(0_a));
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_P(LocationTest, NonConstant) {
+    Build(Construct(ty.u32(), Call(Source{{12, 34}}, "dpdx", 1_a)));
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: @location value requires a const-expression, but expression is a runtime-expression)");
+}
+
+TEST_P(LocationTest, Negative) {
+    Build(Expr(-1_a));
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: @location value must be non-negative)");
+}
+
+TEST_P(LocationTest, F32) {
+    Build(Expr(1_f));
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: @location must be an i32 or u32 value)");
+}
+
+TEST_P(LocationTest, AFloat) {
+    Build(Expr(1.0_a));
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: @location must be an i32 or u32 value)");
+}
+
+INSTANTIATE_TEST_SUITE_P(LocationTest,
+                         LocationTest,
+                         testing::Values(LocationAttributeType::kEntryPointParameter,
+                                         LocationAttributeType::kEntryPointReturnType,
+                                         LocationAttributeType::kStructureMember));
+
 }  // namespace
 }  // namespace InterpolateTests
 
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index 7c15767..c3911fb 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -61,12 +61,10 @@
 }
 
 TEST_F(ResolverBuiltinTest, ModuleScopeUsage) {
-    GlobalConst("c", ty.f32(), Call(Source{{12, 34}}, "abs", 1._f));
+    GlobalConst("c", ty.f32(), Call(Source{{12, 34}}, "dpdy", 1._f));
 
     EXPECT_FALSE(r()->Resolve());
 
-    // TODO(crbug.com/tint/1581): Once 'abs' is implemented as @const, this will no longer be an
-    // error.
     EXPECT_EQ(
         r()->error(),
         R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index b61151d..afb9f5d 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -15,6 +15,7 @@
 #include "src/tint/resolver/const_eval.h"
 
 #include <algorithm>
+#include <iomanip>
 #include <limits>
 #include <optional>
 #include <string>
@@ -187,14 +188,24 @@
 template <typename NumberT>
 std::string OverflowErrorMessage(NumberT lhs, const char* op, NumberT rhs) {
     std::stringstream ss;
+    ss << std::setprecision(20);
     ss << "'" << lhs.value << " " << op << " " << rhs.value << "' cannot be represented as '"
        << FriendlyName<NumberT>() << "'";
     return ss.str();
 }
 
+template <typename VALUE_TY>
+std::string OverflowErrorMessage(VALUE_TY value, std::string_view target_ty) {
+    std::stringstream ss;
+    ss << std::setprecision(20);
+    ss << "value " << value << " cannot be represented as "
+       << "'" << target_ty << "'";
+    return ss.str();
+}
+
 /// @returns the number of consecutive leading bits in `@p e` set to `@p bit_value_to_count`.
 template <typename T>
-auto CountLeadingBits(T e, T bit_value_to_count) -> std::make_unsigned_t<T> {
+std::make_unsigned_t<T> CountLeadingBits(T e, T bit_value_to_count) {
     using UT = std::make_unsigned_t<T>;
     constexpr UT kNumBits = sizeof(UT) * 8;
     constexpr UT kLeftMost = UT{1} << (kNumBits - 1);
@@ -211,7 +222,7 @@
 
 /// @returns the number of consecutive trailing bits set to `@p bit_value_to_count` in `@p e`
 template <typename T>
-auto CountTrailingBits(T e, T bit_value_to_count) -> std::make_unsigned_t<T> {
+std::make_unsigned_t<T> CountTrailingBits(T e, T bit_value_to_count) {
     using UT = std::make_unsigned_t<T>;
     constexpr UT kNumBits = sizeof(UT) * 8;
     constexpr UT kRightMost = UT{1};
@@ -292,10 +303,9 @@
                 // --- Below this point are the failure cases ---
             } else if constexpr (IsAbstract<FROM>) {
                 // [abstract-numeric -> x] - materialization failure
-                std::stringstream ss;
-                ss << "value " << value << " cannot be represented as ";
-                ss << "'" << builder.FriendlyName(target_ty) << "'";
-                builder.Diagnostics().add_error(tint::diag::System::Resolver, ss.str(), source);
+                builder.Diagnostics().add_error(
+                    tint::diag::System::Resolver,
+                    OverflowErrorMessage(value, builder.FriendlyName(target_ty)), source);
                 return utils::Failure;
             } else if constexpr (IsFloatingPoint<TO>) {
                 // [x -> floating-point] - number not exactly representable
@@ -1570,6 +1580,54 @@
     return r;
 }
 
+ConstEval::Result ConstEval::abs(const sem::Type* ty,
+                                 utils::VectorRef<const sem::Constant*> args,
+                                 const Source&) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto e) {
+            using NumberT = decltype(e);
+            NumberT result;
+            if constexpr (IsUnsignedIntegral<NumberT>) {
+                result = e;
+            } else if constexpr (IsSignedIntegral<NumberT>) {
+                if (e == NumberT::Lowest()) {
+                    result = e;
+                } else {
+                    result = NumberT{std::abs(e)};
+                }
+            } else {
+                result = NumberT{std::abs(e)};
+            }
+            return CreateElement(builder, c0->Type(), result);
+        };
+        return Dispatch_fia_fiu32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::acos(const sem::Type* ty,
+                                  utils::VectorRef<const sem::Constant*> args,
+                                  const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto i) -> ImplResult {
+            using NumberT = decltype(i);
+            if (i < NumberT(-1.0) || i > NumberT(1.0)) {
+                AddError("acos must be called with a value in the range [-1 .. 1] (inclusive)", source);
+                return utils::Failure;
+            }
+            return CreateElement(builder, c0->Type(), NumberT(std::acos(i.value)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::all(const sem::Type* ty,
+                                 utils::VectorRef<const sem::Constant*> args,
+                                 const Source&) {
+    return CreateElement(builder, ty, !args[0]->AnyZero());
+}
+
 ConstEval::Result ConstEval::any(const sem::Type* ty,
                                  utils::VectorRef<const sem::Constant*> args,
                                  const Source&) {
@@ -1582,11 +1640,11 @@
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto i) -> ImplResult {
             using NumberT = decltype(i);
-            if (i.value < NumberT(-1.0) || i.value > NumberT(1.0)) {
-                AddError("asin must be called with a value in the range [-1, 1]", source);
+            if (i < NumberT(-1.0) || i > NumberT(1.0)) {
+                AddError("asin must be called with a value in the range [-1 .. 1] (inclusive)", source);
                 return utils::Failure;
             }
-            return CreateElement(builder, c0->Type(), decltype(i)(std::asin(i.value)));
+            return CreateElement(builder, c0->Type(), NumberT(std::asin(i.value)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
@@ -1628,11 +1686,11 @@
     auto transform = [&](const sem::Constant* c0) {
         auto create = [&](auto i) -> ImplResult {
             using NumberT = decltype(i);
-            if (i.value <= NumberT(-1.0) || i.value >= NumberT(1.0)) {
-                AddError("atanh must be called with a value in the range (-1, 1)", source);
+            if (i <= NumberT(-1.0) || i >= NumberT(1.0)) {
+                AddError("atanh must be called with a value in the range (-1 .. 1) (exclusive)", source);
                 return utils::Failure;
             }
-            return CreateElement(builder, c0->Type(), decltype(i)(std::atanh(i.value)));
+            return CreateElement(builder, c0->Type(), NumberT(std::atanh(i.value)));
         };
         return Dispatch_fa_f32_f16(create, c0);
     };
@@ -1652,6 +1710,18 @@
     return TransformElements(builder, ty, transform, args[0], args[1]);
 }
 
+ConstEval::Result ConstEval::ceil(const sem::Type* ty,
+                                  utils::VectorRef<const sem::Constant*> args,
+                                  const Source&) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto e) {
+            return CreateElement(builder, c0->Type(), decltype(e)(std::ceil(e)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 ConstEval::Result ConstEval::clamp(const sem::Type* ty,
                                    utils::VectorRef<const sem::Constant*> args,
                                    const Source&) {
@@ -1720,6 +1790,61 @@
     return TransformElements(builder, ty, transform, args[0]);
 }
 
+ConstEval::Result ConstEval::extractBits(const sem::Type* ty,
+                                         utils::VectorRef<const sem::Constant*> args,
+                                         const Source& source) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto in_e) -> ImplResult {
+            using NumberT = decltype(in_e);
+            using T = UnwrapNumber<NumberT>;
+            using UT = std::make_unsigned_t<T>;
+            using NumberUT = Number<UT>;
+
+            // Read args that are always scalar
+            NumberUT in_offset = args[1]->As<NumberUT>();
+            NumberUT in_count = args[2]->As<NumberUT>();
+
+            constexpr UT w = sizeof(UT) * 8;
+            if ((in_offset + in_count) > w) {
+                AddError("'offset + 'count' must be less than or equal to the bit width of 'e'",
+                         source);
+                return utils::Failure;
+            }
+
+            // Cast all to unsigned
+            UT e = static_cast<UT>(in_e);
+            UT o = static_cast<UT>(in_offset);
+            UT c = static_cast<UT>(in_count);
+
+            NumberT result;
+            if (c == UT{0}) {
+                // The result is 0 if c is 0
+                result = NumberT{0};
+            } else if (c == w) {
+                // The result is e if c is w
+                result = NumberT{e};
+            } else {
+                // Otherwise, bits 0..c - 1 of the result are copied from bits o..o + c - 1 of e.
+                UT src_mask = ((UT{1} << c) - UT{1}) << o;
+                UT r = (e & src_mask) >> o;
+                if constexpr (IsSignedIntegral<NumberT>) {
+                    // Other bits of the result are the same as bit c - 1 of the result.
+                    // Only need to set other bits if bit at c - 1 of result is 1
+                    if ((r & (UT{1} << (c - UT{1}))) != UT{0}) {
+                        UT dst_mask = src_mask >> o;
+                        r |= (~UT{0} & ~dst_mask);
+                    }
+                }
+
+                result = NumberT{r};
+            }
+            return CreateElement(builder, c0->Type(), result);
+        };
+        return Dispatch_iu32(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 ConstEval::Result ConstEval::firstLeadingBit(const sem::Type* ty,
                                              utils::VectorRef<const sem::Constant*> args,
                                              const Source&) {
@@ -1790,6 +1915,97 @@
     return TransformElements(builder, ty, transform, args[0]);
 }
 
+ConstEval::Result ConstEval::floor(const sem::Type* ty,
+                                   utils::VectorRef<const sem::Constant*> args,
+                                   const Source&) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto e) {
+            return CreateElement(builder, c0->Type(), decltype(e)(std::floor(e)));
+        };
+        return Dispatch_fa_f32_f16(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
+ConstEval::Result ConstEval::insertBits(const sem::Type* ty,
+                                        utils::VectorRef<const sem::Constant*> args,
+                                        const Source& source) {
+    auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
+        auto create = [&](auto in_e, auto in_newbits) -> ImplResult {
+            using NumberT = decltype(in_e);
+            using T = UnwrapNumber<NumberT>;
+            using UT = std::make_unsigned_t<T>;
+            using NumberUT = Number<UT>;
+
+            // Read args that are always scalar
+            NumberUT in_offset = args[2]->As<NumberUT>();
+            NumberUT in_count = args[3]->As<NumberUT>();
+
+            constexpr UT w = sizeof(UT) * 8;
+            if ((in_offset + in_count) > w) {
+                AddError("'offset + 'count' must be less than or equal to the bit width of 'e'",
+                         source);
+                return utils::Failure;
+            }
+
+            // Cast all to unsigned
+            UT e = static_cast<UT>(in_e);
+            UT newbits = static_cast<UT>(in_newbits);
+            UT o = static_cast<UT>(in_offset);
+            UT c = static_cast<UT>(in_count);
+
+            NumberT result;
+            if (c == UT{0}) {
+                // The result is e if c is 0
+                result = NumberT{e};
+            } else if (c == w) {
+                // The result is newbits if c is w
+                result = NumberT{newbits};
+            } else {
+                // Otherwise, bits o..o + c - 1 of the result are copied from bits 0..c - 1 of
+                // newbits. Other bits of the result are copied from e.
+                UT from = newbits << o;
+                UT mask = ((UT{1} << c) - UT{1}) << UT{o};
+                auto r = e;          // Start with 'e' as the result
+                r &= ~mask;          // Zero the bits in 'e' we're overwriting
+                r |= (from & mask);  // Overwrite from 'newbits' (shifted into position)
+                result = NumberT{r};
+            }
+
+            return CreateElement(builder, c0->Type(), result);
+        };
+        return Dispatch_iu32(create, c0, c1);
+    };
+    return TransformElements(builder, ty, transform, args[0], args[1]);
+}
+
+ConstEval::Result ConstEval::reverseBits(const sem::Type* ty,
+                                         utils::VectorRef<const sem::Constant*> args,
+                                         const Source&) {
+    auto transform = [&](const sem::Constant* c0) {
+        auto create = [&](auto in_e) -> ImplResult {
+            using NumberT = decltype(in_e);
+            using T = UnwrapNumber<NumberT>;
+            using UT = std::make_unsigned_t<T>;
+            constexpr UT kNumBits = sizeof(UT) * 8;
+
+            UT e = static_cast<UT>(in_e);
+            UT r = UT{0};
+            for (size_t s = 0; s < kNumBits; ++s) {
+                // Write source 's' bit to destination 'd' bit if 1
+                if (e & (UT{1} << s)) {
+                    size_t d = kNumBits - s - 1;
+                    r |= (UT{1} << d);
+                }
+            }
+
+            return CreateElement(builder, c0->Type(), NumberT{r});
+        };
+        return Dispatch_iu32(create, c0);
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 ConstEval::Result ConstEval::saturate(const sem::Type* ty,
                                       utils::VectorRef<const sem::Constant*> args,
                                       const Source&) {
@@ -1869,6 +2085,27 @@
     return TransformElements(builder, ty, transform, args[0], args[1]);
 }
 
+ConstEval::Result ConstEval::quantizeToF16(const sem::Type* ty,
+                                           utils::VectorRef<const sem::Constant*> args,
+                                           const Source&) {
+    auto transform = [&](const sem::Constant* c) {
+        auto conv = CheckedConvert<f32>(f16(c->As<f32>()));
+        if (!conv) {
+            // https://www.w3.org/TR/WGSL/#quantizeToF16-builtin
+            // If e is outside the finite range of binary16, then the result is any value of type
+            // f32
+            switch (conv.Failure()) {
+                case ConversionFailure::kExceedsNegativeLimit:
+                    return CreateElement(builder, c->Type(), f16(f16::kLowestValue));
+                case ConversionFailure::kExceedsPositiveLimit:
+                    return CreateElement(builder, c->Type(), f16(f16::kHighestValue));
+            }
+        }
+        return CreateElement(builder, c->Type(), conv.Get());
+    };
+    return TransformElements(builder, ty, transform, args[0]);
+}
+
 ConstEval::Result ConstEval::Convert(const sem::Type* target_ty,
                                      const sem::Constant* value,
                                      const Source& source) {
diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h
index 91189fa..f309150 100644
--- a/src/tint/resolver/const_eval.h
+++ b/src/tint/resolver/const_eval.h
@@ -377,6 +377,33 @@
     // Builtins
     ////////////////////////////////////////////////////////////////////////////
 
+    /// abs builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result abs(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// all builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result all(const sem::Type* ty,
+               utils::VectorRef<const sem::Constant*> args,
+               const Source& source);
+
+    /// acos builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result acos(const sem::Type* ty,
+                utils::VectorRef<const sem::Constant*> args,
+                const Source& source);
+
     /// any builtin
     /// @param ty the expression type
     /// @param args the input arguments
@@ -431,6 +458,15 @@
                  utils::VectorRef<const sem::Constant*> args,
                  const Source& source);
 
+    /// ceil builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result ceil(const sem::Type* ty,
+                utils::VectorRef<const sem::Constant*> args,
+                const Source& source);
+
     /// clamp builtin
     /// @param ty the expression type
     /// @param args the input arguments
@@ -467,6 +503,15 @@
                               utils::VectorRef<const sem::Constant*> args,
                               const Source& source);
 
+    /// extractBits builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result extractBits(const sem::Type* ty,
+                       utils::VectorRef<const sem::Constant*> args,
+                       const Source& source);
+
     /// firstLeadingBit builtin
     /// @param ty the expression type
     /// @param args the input arguments
@@ -485,6 +530,33 @@
                             utils::VectorRef<const sem::Constant*> args,
                             const Source& source);
 
+    /// floor builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result floor(const sem::Type* ty,
+                 utils::VectorRef<const sem::Constant*> args,
+                 const Source& source);
+
+    /// insertBits builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result insertBits(const sem::Type* ty,
+                      utils::VectorRef<const sem::Constant*> args,
+                      const Source& source);
+
+    /// reverseBits builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result reverseBits(const sem::Type* ty,
+                       utils::VectorRef<const sem::Constant*> args,
+                       const Source& source);
+
     /// saturate builtin
     /// @param ty the expression type
     /// @param args the input arguments
@@ -530,6 +602,15 @@
                 utils::VectorRef<const sem::Constant*> args,
                 const Source& source);
 
+    /// quantizeToF16 builtin
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result quantizeToF16(const sem::Type* ty,
+                         utils::VectorRef<const sem::Constant*> args,
+                         const Source& source);
+
   private:
     /// Adds the given error message to the diagnostics
     void AddError(const std::string& msg, const Source& source) const;
diff --git a/src/tint/resolver/const_eval_binary_op_test.cc b/src/tint/resolver/const_eval_binary_op_test.cc
index 05a1453..8708aa8 100644
--- a/src/tint/resolver/const_eval_binary_op_test.cc
+++ b/src/tint/resolver/const_eval_binary_op_test.cc
@@ -853,7 +853,7 @@
     GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Highest()), AFloat::Highest()));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "1:1 error: '1.79769e+308 + 1.79769e+308' cannot be represented as 'abstract-float'");
+              "1:1 error: '1.7976931348623157081e+308 + 1.7976931348623157081e+308' cannot be represented as 'abstract-float'");
 }
 
 TEST_F(ResolverConstEvalTest, BinaryAbstractAddUnderflow_AFloat) {
@@ -861,7 +861,7 @@
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        "1:1 error: '-1.79769e+308 + -1.79769e+308' cannot be represented as 'abstract-float'");
+        "1:1 error: '-1.7976931348623157081e+308 + -1.7976931348623157081e+308' cannot be represented as 'abstract-float'");
 }
 
 // Mixed AInt and AFloat args to test implicit conversion to AFloat
diff --git a/src/tint/resolver/const_eval_builtin_test.cc b/src/tint/resolver/const_eval_builtin_test.cc
index 3c275cb..3dc395b 100644
--- a/src/tint/resolver/const_eval_builtin_test.cc
+++ b/src/tint/resolver/const_eval_builtin_test.cc
@@ -14,6 +14,8 @@
 
 #include "src/tint/resolver/const_eval_test.h"
 
+#include "src/tint/utils/result.h"
+
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::resolver {
@@ -23,25 +25,39 @@
 using resolver::operator<<;
 
 struct Case {
-    Case(utils::VectorRef<Types> in_args, Types in_expected)
-        : args(std::move(in_args)), expected(std::move(in_expected)) {}
+    Case(utils::VectorRef<Types> in_args, Types expected_value)
+        : args(std::move(in_args)), expected(Success{std::move(expected_value), false, false}) {}
+
+    Case(utils::VectorRef<Types> in_args, const char* expected_err)
+        : args(std::move(in_args)), expected(Failure{expected_err}) {}
 
     /// Expected value may be positive or negative
     Case& PosOrNeg() {
-        expected_pos_or_neg = true;
+        Success s = expected.Get();
+        s.pos_or_neg = true;
+        expected = s;
         return *this;
     }
 
     /// Expected value should be compared using FLOAT_EQ instead of EQ
     Case& FloatComp() {
-        float_compare = true;
+        Success s = expected.Get();
+        s.float_compare = true;
+        expected = s;
         return *this;
     }
 
+    struct Success {
+        Types value;
+        bool pos_or_neg = false;
+        bool float_compare = false;
+    };
+    struct Failure {
+        const char* error = nullptr;
+    };
+
     utils::Vector<Types, 8> args;
-    Types expected;
-    bool expected_pos_or_neg = false;
-    bool float_compare = false;
+    utils::Result<Success, Failure> expected;
 };
 
 static std::ostream& operator<<(std::ostream& o, const Case& c) {
@@ -49,17 +65,24 @@
     for (auto& a : c.args) {
         o << a << ", ";
     }
-    o << "expected: " << c.expected << ", expected_pos_or_neg: " << c.expected_pos_or_neg;
+    o << "expected: ";
+    if (c.expected) {
+        auto s = c.expected.Get();
+        o << s.value << ", pos_or_neg: " << s.pos_or_neg;
+    } else {
+        o << "[ERROR: " << c.expected.Failure().error << "]";
+    }
     return o;
 }
 
+using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>;
+
 /// Creates a Case with Values for args and result
 static Case C(std::initializer_list<Types> args, Types result) {
     return Case{utils::Vector<Types, 8>{args}, std::move(result)};
 }
 
 /// Convenience overload that creates a Case with just scalars
-using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>;
 static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) {
     utils::Vector<Types, 8> args;
     for (auto& sa : sargs) {
@@ -70,6 +93,20 @@
     return Case{std::move(args), std::move(result)};
 }
 
+/// Creates a Case with Values for args and expected error
+static Case E(std::initializer_list<Types> args, const char* err) {
+    return Case{utils::Vector<Types, 8>{args}, err};
+}
+
+/// Convenience overload that creates an expected-error Case with just scalars
+static Case E(std::initializer_list<ScalarTypes> sargs, const char* err) {
+    utils::Vector<Types, 8> args;
+    for (auto& sa : sargs) {
+        std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
+    }
+    return Case{std::move(args), err};
+}
+
 using ResolverConstEvalBuiltinTest = ResolverTestWithParam<std::tuple<sem::BuiltinType, Case>>;
 
 TEST_P(ResolverConstEvalBuiltinTest, Test) {
@@ -83,58 +120,65 @@
         std::visit([&](auto&& v) { args.Push(v.Expr(*this)); }, a);
     }
 
-    auto* expected = ToValueBase(c.expected);
-    auto* expr = Call(sem::str(builtin), std::move(args));
+    auto* expr = Call(Source{{12, 34}}, sem::str(builtin), std::move(args));
 
     GlobalConst("C", expr);
-    auto* expected_expr = expected->Expr(*this);
-    GlobalConst("E", expected_expr);
 
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
+    if (c.expected) {
+        auto expected = c.expected.Get();
 
-    auto* sem = Sem().Get(expr);
-    ASSERT_NE(sem, nullptr);
-    const sem::Constant* value = sem->ConstantValue();
-    ASSERT_NE(value, nullptr);
-    EXPECT_TYPE(value->Type(), sem->Type());
+        auto* expected_expr = ToValueBase(expected.value)->Expr(*this);
+        GlobalConst("E", expected_expr);
 
-    auto* expected_sem = Sem().Get(expected_expr);
-    const sem::Constant* expected_value = expected_sem->ConstantValue();
-    ASSERT_NE(expected_value, nullptr);
-    EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
+        ASSERT_TRUE(r()->Resolve()) << r()->error();
 
-    // @TODO(amaiorano): Rewrite using ScalarArgsFrom()
-    ForEachElemPair(value, expected_value, [&](const sem::Constant* a, const sem::Constant* b) {
-        std::visit(
-            [&](auto&& ct_expected) {
-                using T = typename std::decay_t<decltype(ct_expected)>::ElementType;
+        auto* sem = Sem().Get(expr);
+        ASSERT_NE(sem, nullptr);
+        const sem::Constant* value = sem->ConstantValue();
+        ASSERT_NE(value, nullptr);
+        EXPECT_TYPE(value->Type(), sem->Type());
 
-                auto v = a->As<T>();
-                auto e = b->As<T>();
-                if constexpr (std::is_same_v<bool, T>) {
-                    EXPECT_EQ(v, e);
-                } else if constexpr (IsFloatingPoint<T>) {
-                    if (std::isnan(e)) {
-                        EXPECT_TRUE(std::isnan(v));
-                    } else {
-                        auto vf = (c.expected_pos_or_neg ? Abs(v) : v);
-                        if (c.float_compare) {
-                            EXPECT_FLOAT_EQ(vf, e);
+        auto* expected_sem = Sem().Get(expected_expr);
+        const sem::Constant* expected_value = expected_sem->ConstantValue();
+        ASSERT_NE(expected_value, nullptr);
+        EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
+
+        // @TODO(amaiorano): Rewrite using ScalarArgsFrom()
+        ForEachElemPair(value, expected_value, [&](const sem::Constant* a, const sem::Constant* b) {
+            std::visit(
+                [&](auto&& ct_expected) {
+                    using T = typename std::decay_t<decltype(ct_expected)>::ElementType;
+
+                    auto v = a->As<T>();
+                    auto e = b->As<T>();
+                    if constexpr (std::is_same_v<bool, T>) {
+                        EXPECT_EQ(v, e);
+                    } else if constexpr (IsFloatingPoint<T>) {
+                        if (std::isnan(e)) {
+                            EXPECT_TRUE(std::isnan(v));
                         } else {
-                            EXPECT_EQ(vf, e);
+                            auto vf = (expected.pos_or_neg ? Abs(v) : v);
+                            if (expected.float_compare) {
+                                EXPECT_FLOAT_EQ(vf, e);
+                            } else {
+                                EXPECT_EQ(vf, e);
+                            }
                         }
+                    } else {
+                        EXPECT_EQ((expected.pos_or_neg ? Abs(v) : v), e);
+                        // Check that the constant's integer doesn't contain unexpected
+                        // data in the MSBs that are outside of the bit-width of T.
+                        EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
                     }
-                } else {
-                    EXPECT_EQ((c.expected_pos_or_neg ? Abs(v) : v), e);
-                    // Check that the constant's integer doesn't contain unexpected
-                    // data in the MSBs that are outside of the bit-width of T.
-                    EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
-                }
-            },
-            c.expected);
+                },
+                expected.value);
 
-        return HasFailure() ? Action::kStop : Action::kContinue;
-    });
+            return HasFailure() ? Action::kStop : Action::kContinue;
+        });
+    } else {
+        EXPECT_FALSE(r()->Resolve());
+        EXPECT_EQ(r()->error(), c.expected.Failure().error);
+    }
 }
 
 INSTANTIATE_TEST_SUITE_P(  //
@@ -146,6 +190,82 @@
                          C({1.0_a, 0_a}, kPiOver2<AFloat>),
                      })));
 
+template <typename T, bool finite_only>
+std::vector<Case> AbsCases() {
+    std::vector<Case> cases = {
+        C({T(0)}, T(0)),
+        C({T(2.0)}, T(2.0)),
+        C({T::Highest()}, T::Highest()),
+
+        // Vector tests
+        C({Vec(T(2.0), T::Highest())}, Vec(T(2.0), T::Highest())),
+    };
+
+    ConcatIntoIf<IsSignedIntegral<T>>(
+        cases,
+        std::vector<Case>{
+            C({Negate(T(0))}, T(0)),
+            C({Negate(T(2.0))}, T(2.0)),
+            // If e is signed and is the largest negative, the result is e
+            C({T::Lowest()}, T::Lowest()),
+
+            // 1 more then min i32
+            C({Negate(T(2147483647))}, T(2147483647)),
+
+            C({Vec(T(0), Negate(T(0)))}, Vec(T(0), T(0))),
+            C({Vec(Negate(T(2.0)), T(2.0), T::Highest())}, Vec(T(2.0), T(2.0), T::Highest())),
+        });
+
+    ConcatIntoIf<!finite_only>(cases, std::vector<Case>{
+                                          C({Negate(T::Inf())}, T::Inf()),
+                                          C({T::Inf()}, T::Inf()),
+                                          C({T::NaN()}, T::NaN()),
+                                          C({Vec(Negate(T::Inf()), T::Inf(), T::NaN())},
+                                            Vec(T::Inf(), T::Inf(), T::NaN())),
+                                      });
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Abs,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kAbs),
+                     testing::ValuesIn(Concat(AbsCases<AInt, false>(),  //
+                                              AbsCases<i32, false>(),
+                                              AbsCases<u32, false>(),
+                                              AbsCases<AFloat, true>(),
+                                              AbsCases<f32, false>(),
+                                              AbsCases<f16, false>()))));
+
+static std::vector<Case> AllCases() {
+    return {
+        C({Val(true)}, Val(true)),
+        C({Val(false)}, Val(false)),
+
+        C({Vec(true, true)}, Val(true)),
+        C({Vec(true, false)}, Val(false)),
+        C({Vec(false, true)}, Val(false)),
+        C({Vec(false, false)}, Val(false)),
+
+        C({Vec(true, true, true)}, Val(true)),
+        C({Vec(false, true, true)}, Val(false)),
+        C({Vec(true, false, true)}, Val(false)),
+        C({Vec(true, true, false)}, Val(false)),
+        C({Vec(false, false, false)}, Val(false)),
+
+        C({Vec(true, true, true, true)}, Val(true)),
+        C({Vec(false, true, true, true)}, Val(false)),
+        C({Vec(true, false, true, true)}, Val(false)),
+        C({Vec(true, true, false, true)}, Val(false)),
+        C({Vec(true, true, true, false)}, Val(false)),
+        C({Vec(false, false, false, false)}, Val(false)),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    All,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kAll), testing::ValuesIn(AllCases())));
+
 static std::vector<Case> AnyCases() {
     return {
         C({Val(true)}, Val(true)),
@@ -298,6 +418,19 @@
         C({Vec(T(0.0), T(0.9), -T(0.9))}, Vec(T(0.0), T(1.4722193), -T(1.4722193))).FloatComp(),
     };
 
+    ConcatIntoIf<finite_only>(  //
+        cases,
+        std::vector<Case>{
+            E({1.1_a},
+              "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
+            E({-1.1_a},
+              "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
+            E({T::Inf()},
+              "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
+            E({-T::Inf()},
+              "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
+        });
+
     ConcatIntoIf<!finite_only>(  //
         cases, std::vector<Case>{
                    // If i is NaN, NaN is returned
@@ -317,37 +450,50 @@
                                               AtanhCases<f32, false>(),
                                               AtanhCases<f16, false>()))));
 
-TEST_F(ResolverConstEvalBuiltinTest, Atanh_OutsideRange_Positive) {
-    auto* expr = Call(Source{{12, 24}}, "atanh", Expr(1.0_a));
+template <typename T, bool finite_only>
+std::vector<Case> AcosCases() {
+    std::vector<Case> cases = {
+        // If i is +/-0, +/-0 is returned
+        C({T(0.87758256189)}, T(0.5)).FloatComp(),
 
-    GlobalConst("C", expr);
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:24 error: atanh must be called with a value in the range (-1, 1)");
+        C({T(1.0)}, T(0.0)),
+        C({-T(1.0)}, kPi<T>).FloatComp(),
+
+        // Vector tests
+        C({Vec(T(1.0), -T(1.0))}, Vec(T(0), kPi<T>)).FloatComp(),
+    };
+
+    ConcatIntoIf<finite_only>(  //
+        cases,
+        std::vector<Case>{
+            E({1.1_a},
+              "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
+            E({-1.1_a},
+              "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
+            E({T::Inf()},
+              "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
+            E({-T::Inf()},
+              "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
+        });
+
+    ConcatIntoIf<!finite_only>(  //
+        cases, std::vector<Case>{
+                   // If i is NaN, NaN is returned
+                   C({T::NaN()}, T::NaN()),
+
+                   // Vector tests
+                   C({Vec(T::NaN(), T::NaN())}, Vec(T::NaN(), T::NaN())),
+               });
+
+    return cases;
 }
-
-TEST_F(ResolverConstEvalBuiltinTest, Atanh_OutsideRange_Negative) {
-    auto* expr = Call(Source{{12, 24}}, "atanh", Negation(1.0_a));
-
-    GlobalConst("C", expr);
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:24 error: atanh must be called with a value in the range (-1, 1)");
-}
-
-TEST_F(ResolverConstEvalBuiltinTest, Atanh_OutsideRange_Positive_INF) {
-    auto* expr = Call(Source{{12, 24}}, "atanh", Expr(f32::Inf()));
-
-    GlobalConst("C", expr);
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:24 error: atanh must be called with a value in the range (-1, 1)");
-}
-
-TEST_F(ResolverConstEvalBuiltinTest, Atanh_OutsideRange_Negative_INF) {
-    auto* expr = Call(Source{{12, 24}}, "atanh", Negation(f32::Inf()));
-
-    GlobalConst("C", expr);
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:24 error: atanh must be called with a value in the range (-1, 1)");
-}
+INSTANTIATE_TEST_SUITE_P(  //
+    Acos,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kAcos),
+                     testing::ValuesIn(Concat(AcosCases<AFloat, true>(),  //
+                                              AcosCases<f32, false>(),
+                                              AcosCases<f16, false>()))));
 
 template <typename T, bool finite_only>
 std::vector<Case> AsinCases() {
@@ -363,6 +509,19 @@
         C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver2<T>, -kPiOver2<T>)).FloatComp(),
     };
 
+    ConcatIntoIf<finite_only>(  //
+        cases,
+        std::vector<Case>{
+            E({1.1_a},
+              "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
+            E({-1.1_a},
+              "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
+            E({T::Inf()},
+              "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
+            E({-T::Inf()},
+              "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
+        });
+
     ConcatIntoIf<!finite_only>(  //
         cases, std::vector<Case>{
                    // If i is NaN, NaN is returned
@@ -382,38 +541,6 @@
                                               AsinCases<f32, false>(),
                                               AsinCases<f16, false>()))));
 
-TEST_F(ResolverConstEvalBuiltinTest, Asin_OutsideRange_Positive) {
-    auto* expr = Call(Source{{12, 24}}, "asin", Expr(1.1_a));
-
-    GlobalConst("C", expr);
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:24 error: asin must be called with a value in the range [-1, 1]");
-}
-
-TEST_F(ResolverConstEvalBuiltinTest, Asin_OutsideRange_Negative) {
-    auto* expr = Call(Source{{12, 24}}, "asin", Negation(1.1_a));
-
-    GlobalConst("C", expr);
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:24 error: asin must be called with a value in the range [-1, 1]");
-}
-
-TEST_F(ResolverConstEvalBuiltinTest, Asin_OutsideRange_Positive_INF) {
-    auto* expr = Call(Source{{12, 24}}, "asin", Expr(f32::Inf()));
-
-    GlobalConst("C", expr);
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:24 error: asin must be called with a value in the range [-1, 1]");
-}
-
-TEST_F(ResolverConstEvalBuiltinTest, Asin_OutsideRange_Negative_INF) {
-    auto* expr = Call(Source{{12, 24}}, "asin", Negation(f32::Inf()));
-
-    GlobalConst("C", expr);
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:24 error: asin must be called with a value in the range [-1, 1]");
-}
-
 template <typename T, bool finite_only>
 std::vector<Case> AsinhCases() {
     std::vector<Case> cases = {
@@ -454,6 +581,37 @@
                                               AsinhCases<f32, false>(),
                                               AsinhCases<f16, false>()))));
 
+template <typename T, bool finite_only>
+std::vector<Case> CeilCases() {
+    std::vector<Case> cases = {
+        C({T(0)}, T(0)),
+        C({-T(0)}, -T(0)),
+        C({-T(1.5)}, -T(1.0)),
+        C({T(1.5)}, T(2.0)),
+        C({T::Lowest()}, T::Lowest()),
+        C({T::Highest()}, T::Highest()),
+
+        C({Vec(T(0), T(1.5), -T(1.5))}, Vec(T(0), T(2.0), -T(1.0))),
+    };
+
+    ConcatIntoIf<!finite_only>(
+        cases, std::vector<Case>{
+                   C({-T::Inf()}, -T::Inf()),
+                   C({T::Inf()}, T::Inf()),
+                   C({T::NaN()}, T::NaN()),
+                   C({Vec(-T::Inf(), T::Inf(), T::NaN())}, Vec(-T::Inf(), T::Inf(), T::NaN())),
+               });
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Ceil,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kCeil),
+                     testing::ValuesIn(Concat(CeilCases<AFloat, true>(),
+                                              CeilCases<f32, false>(),
+                                              CeilCases<f16, false>()))));
+
 template <typename T>
 std::vector<Case> ClampCases() {
     return {
@@ -713,6 +871,274 @@
                      testing::ValuesIn(Concat(FirstTrailingBitCases<i32>(),  //
                                               FirstTrailingBitCases<u32>()))));
 
+template <typename T, bool finite_only>
+std::vector<Case> FloorCases() {
+    std::vector<Case> cases = {
+        C({T(0)}, T(0)),
+        C({-T(0)}, -T(0)),
+        C({-T(1.5)}, -T(2.0)),
+        C({T(1.5)}, T(1.0)),
+        C({T::Lowest()}, T::Lowest()),
+        C({T::Highest()}, T::Highest()),
+
+        C({Vec(T(0), T(1.5), -T(1.5))}, Vec(T(0), T(1.0), -T(2.0))),
+    };
+
+    ConcatIntoIf<!finite_only>(
+        cases, std::vector<Case>{
+                   C({-T::Inf()}, -T::Inf()),
+                   C({T::Inf()}, T::Inf()),
+                   C({T::NaN()}, T::NaN()),
+                   C({Vec(-T::Inf(), T::Inf(), T::NaN())}, Vec(-T::Inf(), T::Inf(), T::NaN())),
+               });
+
+    return cases;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Floor,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kFloor),
+                     testing::ValuesIn(Concat(FloorCases<AFloat, true>(),
+                                              FloorCases<f32, false>(),
+                                              FloorCases<f16, false>()))));
+
+template <typename T>
+std::vector<Case> InsertBitsCases() {
+    using UT = Number<std::make_unsigned_t<UnwrapNumber<T>>>;
+
+    auto e = /* */ T(0b0101'1100'0011'1010'0101'1100'0011'1010);
+    auto newbits = T{0b1010'0011'1100'0101'1010'0011'1100'0101};
+
+    auto r = std::vector<Case>{
+        // args: e, newbits, offset, count
+
+        // If count is 0, result is e
+        C({e, newbits, UT(0), UT(0)}, e),  //
+        C({e, newbits, UT(1), UT(0)}, e),  //
+        C({e, newbits, UT(2), UT(0)}, e),  //
+        C({e, newbits, UT(3), UT(0)}, e),  //
+        // ...
+        C({e, newbits, UT(29), UT(0)}, e),  //
+        C({e, newbits, UT(30), UT(0)}, e),  //
+        C({e, newbits, UT(31), UT(0)}, e),
+
+        // Copy 1 to 32 bits of newbits to e at offset 0
+        C({e, newbits, UT(0), UT(1)}, T(0b0101'1100'0011'1010'0101'1100'0011'1011)),
+        C({e, newbits, UT(0), UT(2)}, T(0b0101'1100'0011'1010'0101'1100'0011'1001)),
+        C({e, newbits, UT(0), UT(3)}, T(0b0101'1100'0011'1010'0101'1100'0011'1101)),
+        C({e, newbits, UT(0), UT(4)}, T(0b0101'1100'0011'1010'0101'1100'0011'0101)),
+        C({e, newbits, UT(0), UT(5)}, T(0b0101'1100'0011'1010'0101'1100'0010'0101)),
+        C({e, newbits, UT(0), UT(6)}, T(0b0101'1100'0011'1010'0101'1100'0000'0101)),
+        // ...
+        C({e, newbits, UT(0), UT(29)}, T(0b0100'0011'1100'0101'1010'0011'1100'0101)),
+        C({e, newbits, UT(0), UT(30)}, T(0b0110'0011'1100'0101'1010'0011'1100'0101)),
+        C({e, newbits, UT(0), UT(31)}, T(0b0010'0011'1100'0101'1010'0011'1100'0101)),
+        C({e, newbits, UT(0), UT(32)}, T(0b1010'0011'1100'0101'1010'0011'1100'0101)),
+
+        // Copy at varying offsets and counts
+        C({e, newbits, UT(3), UT(8)}, T(0b0101'1100'0011'1010'0101'1110'0010'1010)),
+        C({e, newbits, UT(8), UT(8)}, T(0b0101'1100'0011'1010'1100'0101'0011'1010)),
+        C({e, newbits, UT(15), UT(1)}, T(0b0101'1100'0011'1010'1101'1100'0011'1010)),
+        C({e, newbits, UT(16), UT(16)}, T(0b1010'0011'1100'0101'0101'1100'0011'1010)),
+
+        // Vector tests
+        C({Vec(T(0b1111'0000'1111'0000'1111'0000'1111'0000),  //
+               T(0b0000'1111'0000'1111'0000'1111'0000'1111),  //
+               T(0b1010'0101'1010'0101'1010'0101'1010'0101)),
+           Vec(T(0b1111'1111'1111'1111'1111'1111'1111'1111),  //
+               T(0b1111'1111'1111'1111'1111'1111'1111'1111),  //
+               T(0b1111'1111'1111'1111'1111'1111'1111'1111)),
+           Val(UT(3)), Val(UT(8))},
+          Vec(T(0b1111'0000'1111'0000'1111'0111'1111'1000),  //
+              T(0b0000'1111'0000'1111'0000'1111'1111'1111),  //
+              T(0b1010'0101'1010'0101'1010'0111'1111'1101))),
+    };
+
+    return r;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    InsertBits,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kInsertBits),
+                     testing::ValuesIn(Concat(InsertBitsCases<i32>(),  //
+                                              InsertBitsCases<u32>()))));
+
+using ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount =
+    ResolverTestWithParam<std::tuple<size_t, size_t>>;
+TEST_P(ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount, Test) {
+    auto& p = GetParam();
+    auto* expr = Call(Source{{12, 34}}, sem::str(sem::BuiltinType::kInsertBits), Expr(1_u),
+                      Expr(1_u), Expr(u32(std::get<0>(p))), Expr(u32(std::get<1>(p))));
+    GlobalConst("C", expr);
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'");
+}
+INSTANTIATE_TEST_SUITE_P(InsertBits,
+                         ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount,
+                         testing::Values(                         //
+                             std::make_tuple(33, 0),              //
+                             std::make_tuple(34, 0),              //
+                             std::make_tuple(1000, 0),            //
+                             std::make_tuple(u32::Highest(), 0),  //
+                             std::make_tuple(0, 33),              //
+                             std::make_tuple(0, 34),              //
+                             std::make_tuple(0, 1000),            //
+                             std::make_tuple(0, u32::Highest()),  //
+                             std::make_tuple(33, 33),             //
+                             std::make_tuple(34, 34),             //
+                             std::make_tuple(1000, 1000),         //
+                             std::make_tuple(u32::Highest(), u32::Highest())));
+
+template <typename T>
+std::vector<Case> ExtractBitsCases() {
+    using UT = Number<std::make_unsigned_t<UnwrapNumber<T>>>;
+
+    // If T is signed, fills most significant bits of `val` with 1s
+    auto set_msbs_if_signed = [](T val) {
+        if constexpr (IsSignedIntegral<T>) {
+            T result = T(~0);
+            for (size_t b = 0; val; ++b) {
+                if ((val & 1) == 0) {
+                    result = result & ~(1 << b);  // Clear bit b
+                }
+                val = val >> 1;
+            }
+            return result;
+        } else {
+            return val;
+        }
+    };
+
+    auto e = T(0b10100011110001011010001111000101);
+    auto f = T(0b01010101010101010101010101010101);
+    auto g = T(0b11111010001111000101101000111100);
+
+    auto r = std::vector<Case>{
+        // args: e, offset, count
+
+        // If count is 0, result is 0
+        C({e, UT(0), UT(0)}, T(0)),  //
+        C({e, UT(1), UT(0)}, T(0)),  //
+        C({e, UT(2), UT(0)}, T(0)),  //
+        C({e, UT(3), UT(0)}, T(0)),
+        // ...
+        C({e, UT(29), UT(0)}, T(0)),  //
+        C({e, UT(30), UT(0)}, T(0)),  //
+        C({e, UT(31), UT(0)}, T(0)),
+
+        // Extract at offset 0, varying counts
+        C({e, UT(0), UT(1)}, set_msbs_if_signed(T(0b1))),    //
+        C({e, UT(0), UT(2)}, T(0b01)),                       //
+        C({e, UT(0), UT(3)}, set_msbs_if_signed(T(0b101))),  //
+        C({e, UT(0), UT(4)}, T(0b0101)),                     //
+        C({e, UT(0), UT(5)}, T(0b00101)),                    //
+        C({e, UT(0), UT(6)}, T(0b000101)),                   //
+        // ...
+        C({e, UT(0), UT(28)}, T(0b0011110001011010001111000101)),                        //
+        C({e, UT(0), UT(29)}, T(0b00011110001011010001111000101)),                       //
+        C({e, UT(0), UT(30)}, set_msbs_if_signed(T(0b100011110001011010001111000101))),  //
+        C({e, UT(0), UT(31)}, T(0b0100011110001011010001111000101)),                     //
+        C({e, UT(0), UT(32)}, T(0b10100011110001011010001111000101)),                    //
+
+        // Extract at varying offsets and counts
+        C({e, UT(0), UT(1)}, set_msbs_if_signed(T(0b1))),                   //
+        C({e, UT(31), UT(1)}, set_msbs_if_signed(T(0b1))),                  //
+        C({e, UT(3), UT(5)}, set_msbs_if_signed(T(0b11000))),               //
+        C({e, UT(4), UT(7)}, T(0b0111100)),                                 //
+        C({e, UT(10), UT(16)}, set_msbs_if_signed(T(0b1111000101101000))),  //
+        C({e, UT(10), UT(22)}, set_msbs_if_signed(T(0b1010001111000101101000))),
+
+        // Vector tests
+        C({Vec(e, f, g),                          //
+           Val(UT(5)), Val(UT(8))},               //
+          Vec(T(0b00011110),                      //
+              set_msbs_if_signed(T(0b10101010)),  //
+              set_msbs_if_signed(T(0b11010001)))),
+    };
+
+    return r;
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    ExtractBits,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kExtractBits),
+                     testing::ValuesIn(Concat(ExtractBitsCases<i32>(),  //
+                                              ExtractBitsCases<u32>()))));
+
+using ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount =
+    ResolverTestWithParam<std::tuple<size_t, size_t>>;
+TEST_P(ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount, Test) {
+    auto& p = GetParam();
+    auto* expr = Call(Source{{12, 34}}, sem::str(sem::BuiltinType::kExtractBits), Expr(1_u),
+                      Expr(u32(std::get<0>(p))), Expr(u32(std::get<1>(p))));
+    GlobalConst("C", expr);
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'");
+}
+INSTANTIATE_TEST_SUITE_P(ExtractBits,
+                         ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount,
+                         testing::Values(                         //
+                             std::make_tuple(33, 0),              //
+                             std::make_tuple(34, 0),              //
+                             std::make_tuple(1000, 0),            //
+                             std::make_tuple(u32::Highest(), 0),  //
+                             std::make_tuple(0, 33),              //
+                             std::make_tuple(0, 34),              //
+                             std::make_tuple(0, 1000),            //
+                             std::make_tuple(0, u32::Highest()),  //
+                             std::make_tuple(33, 33),             //
+                             std::make_tuple(34, 34),             //
+                             std::make_tuple(1000, 1000),         //
+                             std::make_tuple(u32::Highest(), u32::Highest())));
+
+template <typename T>
+std::vector<Case> ReverseBitsCases() {
+    using B = BitValues<T>;
+    return {
+        C({T(0)}, T(0)),
+
+        C({B::Lsh(1, 0)}, B::Lsh(1, 31)),  //
+        C({B::Lsh(1, 1)}, B::Lsh(1, 30)),  //
+        C({B::Lsh(1, 2)}, B::Lsh(1, 29)),  //
+        C({B::Lsh(1, 3)}, B::Lsh(1, 28)),  //
+        C({B::Lsh(1, 4)}, B::Lsh(1, 27)),  //
+        //...
+        C({B::Lsh(1, 27)}, B::Lsh(1, 4)),  //
+        C({B::Lsh(1, 28)}, B::Lsh(1, 3)),  //
+        C({B::Lsh(1, 29)}, B::Lsh(1, 2)),  //
+        C({B::Lsh(1, 30)}, B::Lsh(1, 1)),  //
+        C({B::Lsh(1, 31)}, B::Lsh(1, 0)),  //
+
+        C({/**/ T(0b00010001000100010000000000000000)},
+          /* */ T(0b00000000000000001000100010001000)),
+
+        C({/**/ T(0b00011000000110000000000000000000)},
+          /* */ T(0b00000000000000000001100000011000)),
+
+        C({/**/ T(0b00000100000000001111111111111111)},
+          /* */ T(0b11111111111111110000000000100000)),
+
+        C({/**/ T(0b10010101111000110000011111101010)},
+          /* */ T(0b01010111111000001100011110101001)),
+
+        // Vector tests
+        C({/**/ Vec(T(0b00010001000100010000000000000000),  //
+                    T(0b00011000000110000000000000000000),  //
+                    T(0b00000000000000001111111111111111))},
+          /* */ Vec(T(0b00000000000000001000100010001000),  //
+                    T(0b00000000000000000001100000011000),  //
+                    T(0b11111111111111110000000000000000))),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    ReverseBits,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kReverseBits),
+                     testing::ValuesIn(Concat(ReverseBitsCases<i32>(),  //
+                                              ReverseBitsCases<u32>()))));
+
 template <typename T>
 std::vector<Case> SaturateCases() {
     return {
@@ -842,5 +1268,63 @@
                                               StepCases<f32>(),
                                               StepCases<f16>()))));
 
+std::vector<Case> QuantizeToF16Cases() {
+    (void)E({Vec(0_f, 0_f)}, "");  // Currently unused, but will be soon.
+    return {
+        C({0_f}, 0_f),    //
+        C({-0_f}, -0_f),  //
+        C({1_f}, 1_f),    //
+        C({-1_f}, -1_f),  //
+
+        //   0.00006106496 quantized to 0.000061035156 = 0x1p-14
+        C({0.00006106496_f}, 0.000061035156_f),    //
+        C({-0.00006106496_f}, -0.000061035156_f),  //
+
+        //   1.0004883 quantized to 1.0 = 0x1p0
+        C({1.0004883_f}, 1.0_f),    //
+        C({-1.0004883_f}, -1.0_f),  //
+
+        //   8196.0 quantized to 8192.0 = 0x1p13
+        C({8196_f}, 8192_f),    //
+        C({-8196_f}, -8192_f),  //
+
+        // Value in subnormal f16 range
+        C({0x0.034p-14_f}, 0x0.034p-14_f),    //
+        C({-0x0.034p-14_f}, -0x0.034p-14_f),  //
+        C({0x0.068p-14_f}, 0x0.068p-14_f),    //
+        C({-0x0.068p-14_f}, -0x0.068p-14_f),  //
+
+        //   0x0.06b7p-14 quantized to 0x0.068p-14
+        C({0x0.06b7p-14_f}, 0x0.068p-14_f),    //
+        C({-0x0.06b7p-14_f}, -0x0.068p-14_f),  //
+
+        // Value out of f16 range
+        C({65504.003_f}, 65504_f),     //
+        C({-65504.003_f}, -65504_f),   //
+        C({0x1.234p56_f}, 65504_f),    //
+        C({-0x4.321p65_f}, -65504_f),  //
+
+        // Vector tests
+        C({Vec(0_f, -0_f)}, Vec(0_f, -0_f)),  //
+        C({Vec(1_f, -1_f)}, Vec(1_f, -1_f)),  //
+
+        C({Vec(0.00006106496_f, -0.00006106496_f, 1.0004883_f, -1.0004883_f)},
+          Vec(0.000061035156_f, -0.000061035156_f, 1.0_f, -1.0_f)),
+
+        C({Vec(8196_f, 8192_f, 0x0.034p-14_f)}, Vec(8192_f, 8192_f, 0x0.034p-14_f)),
+
+        C({Vec(0x0.034p-14_f, -0x0.034p-14_f, 0x0.068p-14_f, -0x0.068p-14_f)},
+          Vec(0x0.034p-14_f, -0x0.034p-14_f, 0x0.068p-14_f, -0x0.068p-14_f)),
+
+        C({Vec(65504.003_f, 0x1.234p56_f)}, Vec(65504_f, 65504_f)),
+        C({Vec(-0x1.234p56_f, -65504.003_f)}, Vec(-65504_f, -65504_f)),
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    QuantizeToF16,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kQuantizeToF16),
+                     testing::ValuesIn(QuantizeToF16Cases())));
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index c6d3ad4..ddce1ba 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -2777,12 +2777,12 @@
   /* [33] */ 45,
   /* [34] */ 5,
   /* [35] */ 6,
-  /* [36] */ 23,
-  /* [37] */ 0,
-  /* [38] */ 4,
-  /* [39] */ 44,
-  /* [40] */ 5,
-  /* [41] */ 6,
+  /* [36] */ 44,
+  /* [37] */ 5,
+  /* [38] */ 6,
+  /* [39] */ 23,
+  /* [40] */ 0,
+  /* [41] */ 4,
   /* [42] */ 43,
   /* [43] */ 5,
   /* [44] */ 6,
@@ -2843,34 +2843,34 @@
   /* [99] */ 42,
   /* [100] */ 3,
   /* [101] */ 6,
-  /* [102] */ 12,
+  /* [102] */ 13,
   /* [103] */ 9,
   /* [104] */ 12,
-  /* [105] */ 1,
+  /* [105] */ 0,
   /* [106] */ 12,
-  /* [107] */ 10,
+  /* [107] */ 1,
   /* [108] */ 12,
-  /* [109] */ 7,
+  /* [109] */ 9,
   /* [110] */ 12,
-  /* [111] */ 0,
+  /* [111] */ 10,
   /* [112] */ 11,
   /* [113] */ 0,
   /* [114] */ 12,
-  /* [115] */ 8,
+  /* [115] */ 7,
   /* [116] */ 12,
-  /* [117] */ 4,
-  /* [118] */ 13,
-  /* [119] */ 0,
+  /* [117] */ 8,
+  /* [118] */ 12,
+  /* [119] */ 4,
   /* [120] */ 11,
   /* [121] */ 4,
   /* [122] */ 11,
   /* [123] */ 1,
   /* [124] */ 11,
   /* [125] */ 8,
-  /* [126] */ 11,
-  /* [127] */ 7,
-  /* [128] */ 13,
-  /* [129] */ 9,
+  /* [126] */ 13,
+  /* [127] */ 0,
+  /* [128] */ 11,
+  /* [129] */ 7,
   /* [130] */ 11,
   /* [131] */ 10,
   /* [132] */ 11,
@@ -2879,10 +2879,10 @@
   /* [135] */ 0,
   /* [136] */ 30,
   /* [137] */ 0,
-  /* [138] */ 31,
-  /* [139] */ 0,
-  /* [140] */ 13,
-  /* [141] */ 1,
+  /* [138] */ 13,
+  /* [139] */ 1,
+  /* [140] */ 31,
+  /* [141] */ 0,
   /* [142] */ 32,
   /* [143] */ 0,
   /* [144] */ 33,
@@ -2949,17 +2949,17 @@
   /* [205] */ 10,
   /* [206] */ 21,
   /* [207] */ 0,
-  /* [208] */ 30,
-  /* [209] */ 9,
-  /* [210] */ 51,
-  /* [211] */ 0,
+  /* [208] */ 51,
+  /* [209] */ 0,
+  /* [210] */ 30,
+  /* [211] */ 9,
   /* [212] */ 31,
   /* [213] */ 9,
   /* [214] */ 32,
   /* [215] */ 9,
-  /* [216] */ 33,
+  /* [216] */ 21,
   /* [217] */ 9,
-  /* [218] */ 21,
+  /* [218] */ 33,
   /* [219] */ 9,
   /* [220] */ 21,
   /* [221] */ 10,
@@ -3346,12 +3346,12 @@
   {
     /* [71] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [72] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* matcher indices */ &kMatcherIndices[214],
   },
   {
     /* [73] */
@@ -3365,23 +3365,23 @@
   },
   {
     /* [75] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [76] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [77] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [78] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[212],
   },
   {
     /* [79] */
@@ -3395,53 +3395,53 @@
   },
   {
     /* [81] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [82] */
     /* usage */ ParameterUsage::kDdx,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
-    /* [83] */
+    /* [82] */
     /* usage */ ParameterUsage::kDdy,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
+    /* [83] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
+  },
+  {
     /* [84] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [85] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [86] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [87] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kArrayIndex,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [88] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [89] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [90] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[216],
+    /* matcher indices */ &kMatcherIndices[214],
   },
   {
     /* [91] */
@@ -3451,82 +3451,82 @@
   {
     /* [92] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [93] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [94] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [95] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[108],
-  },
-  {
-    /* [96] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [97] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [98] */
-    /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
-    /* [99] */
+    /* [93] */
     /* usage */ ParameterUsage::kArrayIndex,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [100] */
-    /* usage */ ParameterUsage::kLevel,
+    /* [94] */
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [95] */
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [96] */
+    /* usage */ ParameterUsage::kComponent,
     /* matcher indices */ &kMatcherIndices[12],
   },
   {
+    /* [97] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[142],
+  },
+  {
+    /* [98] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [99] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [100] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[20],
+  },
+  {
     /* [101] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [102] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [103] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [104] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [105] */
-    /* usage */ ParameterUsage::kArrayIndex,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [106] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [107] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [108] */
@@ -3556,97 +3556,97 @@
   {
     /* [113] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [114] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [115] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [116] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [117] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kArrayIndex,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [118] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [119] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [120] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [121] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [122] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [123] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [124] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [125] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[114],
   },
   {
     /* [126] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [127] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [128] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [129] */
-    /* usage */ ParameterUsage::kArrayIndex,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [130] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [131] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [132] */
@@ -3676,12 +3676,12 @@
   {
     /* [137] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [138] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[224],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [139] */
@@ -3691,7 +3691,7 @@
   {
     /* [140] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [141] */
@@ -3700,48 +3700,48 @@
   },
   {
     /* [142] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [143] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [144] */
-    /* usage */ ParameterUsage::kComponent,
+    /* usage */ ParameterUsage::kLevel,
     /* matcher indices */ &kMatcherIndices[12],
   },
   {
-    /* [145] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* [143] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
-    /* [146] */
+    /* [144] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[224],
+  },
+  {
+    /* [145] */
     /* usage */ ParameterUsage::kSampler,
     /* matcher indices */ &kMatcherIndices[230],
   },
   {
-    /* [147] */
+    /* [146] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [147] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [148] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [149] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [150] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[224],
   },
   {
     /* [151] */
@@ -3751,7 +3751,7 @@
   {
     /* [152] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [153] */
@@ -3765,43 +3765,43 @@
   },
   {
     /* [155] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [156] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[138],
-  },
-  {
-    /* [157] */
     /* usage */ ParameterUsage::kSampler,
     /* matcher indices */ &kMatcherIndices[230],
   },
   {
-    /* [158] */
+    /* [157] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [158] */
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [159] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[114],
   },
   {
     /* [160] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[214],
   },
   {
     /* [161] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [162] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [163] */
@@ -3810,68 +3810,68 @@
   },
   {
     /* [164] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
-  },
-  {
-    /* [165] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [166] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [167] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [168] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [169] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
-  },
-  {
-    /* [170] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[216],
-  },
-  {
-    /* [171] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [172] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [173] */
     /* usage */ ParameterUsage::kLevel,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
-    /* [174] */
+    /* [165] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[12],
+  },
+  {
+    /* [166] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[140],
+  },
+  {
+    /* [167] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [168] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [169] */
     /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
+  },
+  {
+    /* [170] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
+  },
+  {
+    /* [171] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[231],
+  },
+  {
+    /* [172] */
+    /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[108],
   },
   {
+    /* [173] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [174] */
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
     /* [175] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [176] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [177] */
@@ -3880,38 +3880,38 @@
   },
   {
     /* [178] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [179] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
-  },
-  {
-    /* [180] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
-  },
-  {
-    /* [181] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
-  },
-  {
-    /* [182] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [183] */
     /* usage */ ParameterUsage::kDepthRef,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
-    /* [184] */
+    /* [179] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[128],
+  },
+  {
+    /* [180] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
+  },
+  {
+    /* [181] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [182] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [183] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [184] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[12],
   },
   {
     /* [185] */
@@ -3955,18 +3955,18 @@
   },
   {
     /* [193] */
-    /* usage */ ParameterUsage::kBias,
+    /* usage */ ParameterUsage::kLevel,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [194] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [195] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [196] */
@@ -3976,47 +3976,47 @@
   {
     /* [197] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [198] */
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [199] */
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [200] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
+  },
+  {
+    /* [201] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[231],
+  },
+  {
+    /* [202] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [203] */
     /* usage */ ParameterUsage::kArrayIndex,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [199] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
-  },
-  {
-    /* [200] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [201] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
-  },
-  {
-    /* [202] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [203] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
     /* [204] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [205] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[224],
+    /* matcher indices */ &kMatcherIndices[214],
   },
   {
     /* [206] */
@@ -4026,7 +4026,7 @@
   {
     /* [207] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [208] */
@@ -4035,13 +4035,13 @@
   },
   {
     /* [209] */
-    /* usage */ ParameterUsage::kLevel,
+    /* usage */ ParameterUsage::kBias,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [210] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[216],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [211] */
@@ -4051,22 +4051,22 @@
   {
     /* [212] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [213] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [214] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[12],
   },
   {
     /* [215] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[212],
   },
   {
     /* [216] */
@@ -4080,68 +4080,68 @@
   },
   {
     /* [218] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [219] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [220] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [221] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [222] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [223] */
-    /* usage */ ParameterUsage::kArrayIndex,
+    /* usage */ ParameterUsage::kLevel,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [224] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [225] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[12],
   },
   {
     /* [226] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [227] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [228] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
-    /* [228] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
-  },
-  {
     /* [229] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[20],
   },
   {
     /* [230] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[224],
   },
   {
     /* [231] */
@@ -4151,7 +4151,7 @@
   {
     /* [232] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [233] */
@@ -4166,7 +4166,7 @@
   {
     /* [235] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[224],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [236] */
@@ -4176,7 +4176,7 @@
   {
     /* [237] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [238] */
@@ -4185,18 +4185,18 @@
   },
   {
     /* [239] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [240] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [241] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [242] */
@@ -4205,18 +4205,18 @@
   },
   {
     /* [243] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [244] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [245] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [246] */
@@ -4226,7 +4226,7 @@
   {
     /* [247] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [248] */
@@ -4266,7 +4266,7 @@
   {
     /* [255] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [256] */
@@ -4276,7 +4276,7 @@
   {
     /* [257] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [258] */
@@ -4291,89 +4291,89 @@
   {
     /* [260] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [261] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [262] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [263] */
-    /* usage */ ParameterUsage::kDepthRef,
+    /* usage */ ParameterUsage::kLevel,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [264] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* matcher indices */ &kMatcherIndices[114],
   },
   {
     /* [265] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [266] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
-  },
-  {
-    /* [267] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [268] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [269] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[20],
-  },
-  {
-    /* [270] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[216],
-  },
-  {
-    /* [271] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [272] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [273] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [274] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [275] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[234],
   },
   {
-    /* [276] */
+    /* [266] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [267] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [268] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [269] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
+  },
+  {
+    /* [270] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[234],
+  },
+  {
+    /* [271] */
     /* usage */ ParameterUsage::kSampler,
     /* matcher indices */ &kMatcherIndices[231],
   },
   {
+    /* [272] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [273] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [274] */
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [275] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[214],
+  },
+  {
+    /* [276] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
     /* [277] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[132],
@@ -4385,63 +4385,63 @@
   },
   {
     /* [279] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [280] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[222],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [281] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [282] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [283] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [284] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [285] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[12],
   },
   {
     /* [286] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[150],
+  },
+  {
+    /* [287] */
     /* usage */ ParameterUsage::kSampler,
     /* matcher indices */ &kMatcherIndices[230],
   },
   {
-    /* [287] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
     /* [288] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [289] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[20],
   },
   {
     /* [290] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* matcher indices */ &kMatcherIndices[222],
   },
   {
     /* [291] */
@@ -4451,22 +4451,22 @@
   {
     /* [292] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [293] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [294] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [295] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[214],
+    /* matcher indices */ &kMatcherIndices[212],
   },
   {
     /* [296] */
@@ -4480,18 +4480,18 @@
   },
   {
     /* [298] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [299] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [300] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [301] */
@@ -4500,33 +4500,33 @@
   },
   {
     /* [302] */
-    /* usage */ ParameterUsage::kLevel,
+    /* usage */ ParameterUsage::kDepthRef,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [303] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[214],
   },
   {
     /* [304] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [305] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [306] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [307] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [308] */
@@ -4536,57 +4536,57 @@
   {
     /* [309] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [310] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [311] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [312] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [313] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [314] */
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[114],
+  },
+  {
+    /* [315] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
+  },
+  {
+    /* [316] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[231],
+  },
+  {
+    /* [317] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [318] */
     /* usage */ ParameterUsage::kDepthRef,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
-    /* [315] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
-  },
-  {
-    /* [316] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
-  },
-  {
-    /* [317] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
-  },
-  {
-    /* [318] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
-  },
-  {
     /* [319] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[216],
+    /* matcher indices */ &kMatcherIndices[224],
   },
   {
     /* [320] */
@@ -4596,17 +4596,17 @@
   {
     /* [321] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [322] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [323] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[224],
+    /* matcher indices */ &kMatcherIndices[222],
   },
   {
     /* [324] */
@@ -4616,12 +4616,12 @@
   {
     /* [325] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [326] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [327] */
@@ -4631,7 +4631,7 @@
   {
     /* [328] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [329] */
@@ -4640,13 +4640,13 @@
   },
   {
     /* [330] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [331] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [332] */
@@ -4656,7 +4656,7 @@
   {
     /* [333] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [334] */
@@ -4666,7 +4666,7 @@
   {
     /* [335] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [336] */
@@ -4676,117 +4676,117 @@
   {
     /* [337] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [338] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [339] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [340] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [341] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [342] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [343] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
-  },
-  {
-    /* [344] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [345] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [346] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[126],
-  },
-  {
-    /* [347] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
-  },
-  {
-    /* [348] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [349] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [350] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [351] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [352] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
-  },
-  {
-    /* [353] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [354] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
-    /* [355] */
     /* usage */ ParameterUsage::kX,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [356] */
+    /* [340] */
     /* usage */ ParameterUsage::kY,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [357] */
+    /* [341] */
     /* usage */ ParameterUsage::kZ,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [358] */
+    /* [342] */
     /* usage */ ParameterUsage::kW,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
+    /* [343] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[12],
+  },
+  {
+    /* [344] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[140],
+  },
+  {
+    /* [345] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [346] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
+  },
+  {
+    /* [347] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[72],
+  },
+  {
+    /* [348] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[112],
+  },
+  {
+    /* [349] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[12],
+  },
+  {
+    /* [350] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [351] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
+  },
+  {
+    /* [352] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [353] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [354] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [355] */
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[12],
+  },
+  {
+    /* [356] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[148],
+  },
+  {
+    /* [357] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [358] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
     /* [359] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[54],
   },
   {
     /* [360] */
@@ -4800,53 +4800,53 @@
   },
   {
     /* [362] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[152],
   },
   {
     /* [363] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[39],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [364] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [365] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [366] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[156],
-  },
-  {
-    /* [367] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [368] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[138],
-  },
-  {
-    /* [369] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [370] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
+    /* [366] */
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
+  },
+  {
+    /* [367] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[142],
+  },
+  {
+    /* [368] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
+  },
+  {
+    /* [369] */
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[20],
+  },
+  {
+    /* [370] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[67],
+  },
+  {
     /* [371] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[216],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [372] */
@@ -4856,57 +4856,57 @@
   {
     /* [373] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [374] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[62],
-  },
-  {
-    /* [375] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
-  },
-  {
-    /* [376] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [377] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [378] */
     /* usage */ ParameterUsage::kBias,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
+    /* [375] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [376] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [377] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [378] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
     /* [379] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[222],
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [380] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [381] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [382] */
-    /* usage */ ParameterUsage::kLevel,
+    /* usage */ ParameterUsage::kDepthRef,
     /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [383] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[216],
+    /* matcher indices */ &kMatcherIndices[222],
   },
   {
     /* [384] */
@@ -4916,7 +4916,7 @@
   {
     /* [385] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [386] */
@@ -4926,82 +4926,82 @@
   {
     /* [387] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [388] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* matcher indices */ &kMatcherIndices[231],
   },
   {
     /* [389] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [390] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [391] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[222],
+    /* matcher indices */ &kMatcherIndices[36],
   },
   {
     /* [392] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [393] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[12],
   },
   {
     /* [394] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [395] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [396] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [397] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[12],
   },
   {
     /* [398] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[20],
   },
   {
     /* [399] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [400] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [401] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [402] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [403] */
@@ -5026,7 +5026,7 @@
   {
     /* [407] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [408] */
@@ -5036,72 +5036,72 @@
   {
     /* [409] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [410] */
-    /* usage */ ParameterUsage::kLevel,
+    /* usage */ ParameterUsage::kArrayIndex,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [411] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [412] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [413] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [414] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [415] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[72],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [416] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [417] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [418] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [419] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [420] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [421] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [422] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [423] */
@@ -5146,62 +5146,62 @@
   {
     /* [431] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[54],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [432] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [433] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [434] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[152],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [435] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* matcher indices */ &kMatcherIndices[212],
   },
   {
     /* [436] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [437] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [438] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[67],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [439] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[212],
   },
   {
     /* [440] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [441] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [442] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [443] */
@@ -5211,7 +5211,7 @@
   {
     /* [444] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[231],
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [445] */
@@ -5220,118 +5220,118 @@
   },
   {
     /* [446] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [447] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [448] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [449] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [450] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [451] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [452] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [453] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [454] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [455] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [456] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [457] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [458] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [459] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [460] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [461] */
-    /* usage */ ParameterUsage::kZw,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [462] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[210],
   },
   {
     /* [463] */
-    /* usage */ ParameterUsage::kYz,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [464] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [465] */
-    /* usage */ ParameterUsage::kXy,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[212],
   },
   {
     /* [466] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [467] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [468] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [469] */
@@ -5341,37 +5341,37 @@
   {
     /* [470] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [471] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[222],
   },
   {
     /* [472] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [473] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [474] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [475] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
   },
   {
     /* [476] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [477] */
@@ -5421,7 +5421,7 @@
   {
     /* [486] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [487] */
@@ -5431,102 +5431,102 @@
   {
     /* [488] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [489] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[222],
+    /* matcher indices */ &kMatcherIndices[51],
   },
   {
     /* [490] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [491] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[152],
   },
   {
     /* [492] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[144],
+  },
+  {
+    /* [493] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[106],
+  },
+  {
+    /* [494] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[20],
+  },
+  {
+    /* [495] */
     /* usage */ ParameterUsage::kX,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [493] */
+    /* [496] */
     /* usage */ ParameterUsage::kY,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [494] */
+    /* [497] */
     /* usage */ ParameterUsage::kZ,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [495] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[216],
-  },
-  {
-    /* [496] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [497] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
-  },
-  {
     /* [498] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[212],
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [499] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [500] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [501] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[208],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [502] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
+    /* usage */ ParameterUsage::kYz,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [503] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [504] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [505] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [506] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* usage */ ParameterUsage::kZw,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [507] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [508] */
@@ -5536,179 +5536,179 @@
   {
     /* [509] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[102],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [510] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [511] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [512] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [513] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [514] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [515] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
-  },
-  {
-    /* [516] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [517] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [518] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [519] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [520] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [521] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [522] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [523] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [524] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [525] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [526] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [527] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [528] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
-  },
-  {
-    /* [529] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [530] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [531] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
-  },
-  {
-    /* [532] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [533] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [534] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[236],
-  },
-  {
-    /* [535] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[230],
-  },
-  {
-    /* [536] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[132],
-  },
-  {
-    /* [537] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[212],
   },
   {
-    /* [538] */
+    /* [511] */
     /* usage */ ParameterUsage::kSampler,
     /* matcher indices */ &kMatcherIndices[230],
   },
   {
-    /* [539] */
+    /* [512] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
-    /* [540] */
+    /* [513] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[236],
   },
   {
-    /* [541] */
+    /* [514] */
     /* usage */ ParameterUsage::kSampler,
     /* matcher indices */ &kMatcherIndices[230],
   },
   {
-    /* [542] */
+    /* [515] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[132],
   },
   {
-    /* [543] */
+    /* [516] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[99],
   },
   {
+    /* [517] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [518] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [519] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[75],
+  },
+  {
+    /* [520] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[112],
+  },
+  {
+    /* [521] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [522] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[66],
+  },
+  {
+    /* [523] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[104],
+  },
+  {
+    /* [524] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[102],
+  },
+  {
+    /* [525] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[63],
+  },
+  {
+    /* [526] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [527] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[152],
+  },
+  {
+    /* [528] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[57],
+  },
+  {
+    /* [529] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[112],
+  },
+  {
+    /* [530] */
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[152],
+  },
+  {
+    /* [531] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [532] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [533] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [534] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
+  },
+  {
+    /* [535] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [536] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [537] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [538] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [539] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [540] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [541] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [542] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [543] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[48],
+  },
+  {
     /* [544] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[1],
@@ -5716,267 +5716,267 @@
   {
     /* [545] */
     /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [546] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[75],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [547] */
-    /* usage */ ParameterUsage::kCoords,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [548] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [549] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [550] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [551] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
-  },
-  {
-    /* [552] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [553] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [554] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[36],
-  },
-  {
-    /* [555] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[66],
-  },
-  {
-    /* [556] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[110],
-  },
-  {
-    /* [557] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[128],
-  },
-  {
-    /* [558] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[63],
-  },
-  {
-    /* [559] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [560] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[152],
-  },
-  {
-    /* [561] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[57],
-  },
-  {
-    /* [562] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [563] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[152],
-  },
-  {
-    /* [564] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[51],
-  },
-  {
-    /* [565] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[110],
-  },
-  {
-    /* [566] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[152],
-  },
-  {
-    /* [567] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[48],
-  },
-  {
-    /* [568] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [569] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[156],
-  },
-  {
-    /* [570] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[237],
-  },
-  {
-    /* [571] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
-  },
-  {
-    /* [572] */
-    /* usage */ ParameterUsage::kSampleIndex,
-    /* matcher indices */ &kMatcherIndices[12],
-  },
-  {
-    /* [573] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [574] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [575] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
-  },
-  {
-    /* [576] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [577] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [578] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
-    /* [579] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[42],
   },
   {
-    /* [580] */
+    /* [550] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[112],
   },
   {
-    /* [581] */
+    /* [551] */
     /* usage */ ParameterUsage::kValue,
     /* matcher indices */ &kMatcherIndices[156],
   },
   {
-    /* [582] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* [552] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [583] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* [553] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [584] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* [554] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
-    /* [585] */
+    /* [555] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [556] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [557] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[41],
+  },
+  {
+    /* [558] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [559] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [560] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[39],
+  },
+  {
+    /* [561] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
+  },
+  {
+    /* [562] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
+  },
+  {
+    /* [563] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
+  },
+  {
+    /* [564] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[33],
   },
   {
-    /* [586] */
+    /* [565] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[108],
+    /* matcher indices */ &kMatcherIndices[114],
   },
   {
-    /* [587] */
+    /* [566] */
     /* usage */ ParameterUsage::kValue,
     /* matcher indices */ &kMatcherIndices[156],
   },
   {
-    /* [588] */
+    /* [567] */
     /* usage */ ParameterUsage::kTexture,
     /* matcher indices */ &kMatcherIndices[136],
   },
   {
-    /* [589] */
+    /* [568] */
     /* usage */ ParameterUsage::kCoords,
     /* matcher indices */ &kMatcherIndices[12],
   },
   {
-    /* [590] */
+    /* [569] */
     /* usage */ ParameterUsage::kLevel,
     /* matcher indices */ &kMatcherIndices[20],
   },
   {
+    /* [570] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[140],
+  },
+  {
+    /* [571] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
+  },
+  {
+    /* [572] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[20],
+  },
+  {
+    /* [573] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
+  },
+  {
+    /* [574] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[230],
+  },
+  {
+    /* [575] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
+  },
+  {
+    /* [576] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[126],
+  },
+  {
+    /* [577] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[126],
+  },
+  {
+    /* [578] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[126],
+  },
+  {
+    /* [579] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [580] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [581] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [582] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [583] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [584] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
+  },
+  {
+    /* [585] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[235],
+  },
+  {
+    /* [586] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[112],
+  },
+  {
+    /* [587] */
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[12],
+  },
+  {
+    /* [588] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[154],
+  },
+  {
+    /* [589] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
+  },
+  {
+    /* [590] */
+    /* usage */ ParameterUsage::kSampleIndex,
+    /* matcher indices */ &kMatcherIndices[20],
+  },
+  {
     /* [591] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[154],
+    /* matcher indices */ &kMatcherIndices[237],
   },
   {
     /* [592] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [593] */
     /* usage */ ParameterUsage::kSampleIndex,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* matcher indices */ &kMatcherIndices[12],
   },
   {
     /* [594] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [595] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [596] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[20],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [597] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [598] */
@@ -5986,27 +5986,27 @@
   {
     /* [599] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[69],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [600] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [601] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [602] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [603] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [604] */
@@ -6015,33 +6015,33 @@
   },
   {
     /* [605] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [606] */
-    /* usage */ ParameterUsage::kLevel,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [607] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [608] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[4],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [609] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[236],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [610] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [611] */
@@ -6056,12 +6056,12 @@
   {
     /* [613] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[18],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [614] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[22],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [615] */
@@ -6076,12 +6076,12 @@
   {
     /* [617] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [618] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [619] */
@@ -6096,22 +6096,22 @@
   {
     /* [621] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [622] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [623] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[136],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [624] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[12],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [625] */
@@ -6121,12 +6121,12 @@
   {
     /* [626] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [627] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[136],
   },
   {
     /* [628] */
@@ -6136,17 +6136,17 @@
   {
     /* [629] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [630] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[90],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [631] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [632] */
@@ -6156,17 +6156,17 @@
   {
     /* [633] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [634] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [635] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [636] */
@@ -6176,7 +6176,7 @@
   {
     /* [637] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [638] */
@@ -6186,7 +6186,7 @@
   {
     /* [639] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
+    /* matcher indices */ &kMatcherIndices[144],
   },
   {
     /* [640] */
@@ -6196,17 +6196,17 @@
   {
     /* [641] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [642] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [643] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
+    /* matcher indices */ &kMatcherIndices[148],
   },
   {
     /* [644] */
@@ -6216,7 +6216,7 @@
   {
     /* [645] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [646] */
@@ -6225,38 +6225,38 @@
   },
   {
     /* [647] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [648] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[12],
   },
   {
     /* [649] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [650] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [651] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [652] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [653] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [654] */
@@ -6271,12 +6271,12 @@
   {
     /* [656] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [657] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [658] */
@@ -6286,21 +6286,21 @@
   {
     /* [659] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [660] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [661] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [662] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kLevel,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
@@ -6311,57 +6311,57 @@
   {
     /* [664] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [665] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [666] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [667] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [668] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [669] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [670] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [671] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [672] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [673] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[36],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [674] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[36],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [675] */
@@ -6381,92 +6381,92 @@
   {
     /* [678] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [679] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [680] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [681] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[36],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [682] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[36],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [683] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [684] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [685] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [686] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [687] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [688] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[90],
   },
   {
     /* [689] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [690] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [691] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[18],
   },
   {
     /* [692] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[22],
   },
   {
     /* [693] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [694] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [695] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [696] */
@@ -6476,42 +6476,42 @@
   {
     /* [697] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [698] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [699] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [700] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [701] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [702] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [703] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [704] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [705] */
@@ -6521,7 +6521,7 @@
   {
     /* [706] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [707] */
@@ -6531,47 +6531,47 @@
   {
     /* [708] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [709] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [710] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [711] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [712] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [713] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [714] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [715] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [716] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [717] */
@@ -6596,22 +6596,22 @@
   {
     /* [721] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [722] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [723] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [724] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [725] */
@@ -6621,47 +6621,47 @@
   {
     /* [726] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [727] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [728] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* usage */ ParameterUsage::kYz,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [729] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [730] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [731] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [732] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [733] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [734] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [735] */
@@ -6681,7 +6681,7 @@
   {
     /* [738] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[93],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [739] */
@@ -6691,27 +6691,27 @@
   {
     /* [740] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [741] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [742] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[93],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [743] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [744] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [745] */
@@ -6741,7 +6741,7 @@
   {
     /* [750] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [751] */
@@ -6751,16 +6751,16 @@
   {
     /* [752] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[93],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [753] */
-    /* usage */ ParameterUsage::kX,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [754] */
-    /* usage */ ParameterUsage::kY,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
@@ -6771,7 +6771,7 @@
   {
     /* [756] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [757] */
@@ -6781,47 +6781,47 @@
   {
     /* [758] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [759] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [760] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[93],
   },
   {
     /* [761] */
-    /* usage */ ParameterUsage::kXy,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [762] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [763] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [764] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [765] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [766] */
-    /* usage */ ParameterUsage::kYz,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [767] */
@@ -6846,42 +6846,42 @@
   {
     /* [771] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [772] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [773] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [774] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[93],
   },
   {
     /* [775] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [776] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [777] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [778] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [779] */
@@ -6891,16 +6891,16 @@
   {
     /* [780] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[93],
   },
   {
     /* [781] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kX,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [782] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kY,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
@@ -6910,38 +6910,38 @@
   },
   {
     /* [784] */
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [785] */
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[112],
+  },
+  {
+    /* [786] */
     /* usage */ ParameterUsage::kZw,
     /* matcher indices */ &kMatcherIndices[112],
   },
   {
-    /* [785] */
+    /* [787] */
     /* usage */ ParameterUsage::kXyz,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
-    /* [786] */
+    /* [788] */
     /* usage */ ParameterUsage::kW,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [787] */
+    /* [789] */
     /* usage */ ParameterUsage::kX,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
-    /* [788] */
-    /* usage */ ParameterUsage::kZyw,
-    /* matcher indices */ &kMatcherIndices[110],
-  },
-  {
-    /* [789] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
-  },
-  {
     /* [790] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kZyw,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [791] */
@@ -6956,22 +6956,22 @@
   {
     /* [793] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [794] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [795] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [796] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [797] */
@@ -6981,47 +6981,47 @@
   {
     /* [798] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [799] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [800] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [801] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [802] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [803] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [804] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [805] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [806] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [807] */
@@ -7031,82 +7031,82 @@
   {
     /* [808] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[154],
   },
   {
     /* [809] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[233],
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [810] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[233],
   },
   {
     /* [811] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[235],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [812] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
+    /* matcher indices */ &kMatcherIndices[235],
   },
   {
     /* [813] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[148],
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [814] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[144],
+    /* matcher indices */ &kMatcherIndices[148],
   },
   {
     /* [815] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* matcher indices */ &kMatcherIndices[144],
   },
   {
     /* [816] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [817] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[136],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [818] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[45],
+    /* matcher indices */ &kMatcherIndices[136],
   },
   {
     /* [819] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[232],
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [820] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[234],
+    /* matcher indices */ &kMatcherIndices[232],
   },
   {
     /* [821] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[150],
+    /* matcher indices */ &kMatcherIndices[234],
   },
   {
     /* [822] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[142],
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [823] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[236],
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [824] */
@@ -7181,7 +7181,7 @@
   {
     /* [838] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[138],
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [839] */
@@ -7191,7 +7191,7 @@
   {
     /* [840] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[162],
   },
   {
     /* [841] */
@@ -7216,42 +7216,42 @@
   {
     /* [845] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [846] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [847] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[10],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [848] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[10],
   },
   {
     /* [849] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [850] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [851] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [852] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[162],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [853] */
@@ -7261,22 +7261,22 @@
   {
     /* [854] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [855] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [856] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [857] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [858] */
@@ -7286,12 +7286,12 @@
   {
     /* [859] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [860] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[236],
   },
   {
     /* [861] */
@@ -7331,27 +7331,27 @@
   {
     /* [868] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [869] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [870] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[36],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [871] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [872] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [873] */
@@ -7376,47 +7376,47 @@
   {
     /* [877] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [878] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [879] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[128],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [880] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [881] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [882] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[132],
+    /* matcher indices */ &kMatcherIndices[102],
   },
   {
     /* [883] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [884] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [885] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [886] */
@@ -7426,82 +7426,82 @@
   {
     /* [887] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [888] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [889] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [890] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [891] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [892] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [893] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [894] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [895] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [896] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [897] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [898] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [899] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [900] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [901] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [902] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [903] */
@@ -7561,27 +7561,27 @@
   {
     /* [914] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [915] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [916] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [917] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [918] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [919] */
@@ -7591,72 +7591,72 @@
   {
     /* [920] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [921] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [922] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [923] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [924] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [925] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[60],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [926] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [927] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[14],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [928] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [929] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [930] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [931] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [932] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [933] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[4],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [934] */
@@ -7666,72 +7666,72 @@
   {
     /* [935] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[226],
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [936] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[95],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [937] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[226],
   },
   {
     /* [938] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[220],
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [939] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [940] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [941] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[218],
+    /* matcher indices */ &kMatcherIndices[62],
   },
   {
     /* [942] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[6],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [943] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [944] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[6],
   },
   {
     /* [945] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [946] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [947] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [948] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[112],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [949] */
@@ -7741,17 +7741,17 @@
   {
     /* [950] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [951] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [952] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[122],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [953] */
@@ -7771,77 +7771,77 @@
   {
     /* [956] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[206],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [957] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[110],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [958] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[206],
   },
   {
     /* [959] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [960] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [961] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [962] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [963] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [964] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [965] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [966] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[104],
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [967] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[204],
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [968] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[118],
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [969] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[204],
   },
   {
     /* [970] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [971] */
@@ -7851,227 +7851,237 @@
   {
     /* [972] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [973] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [974] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [975] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [976] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [977] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [978] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[140],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [979] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[140],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [980] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[140],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [981] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[140],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [982] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[202],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [983] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[160],
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [984] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [985] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[160],
   },
   {
     /* [986] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[164],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [987] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[166],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [988] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[154],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[164],
   },
   {
     /* [989] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[168],
+    /* matcher indices */ &kMatcherIndices[166],
   },
   {
     /* [990] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[36],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [991] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[168],
   },
   {
     /* [992] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[170],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [993] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[174],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [994] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[36],
+    /* matcher indices */ &kMatcherIndices[170],
   },
   {
     /* [995] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[176],
+    /* matcher indices */ &kMatcherIndices[174],
   },
   {
     /* [996] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[38],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [997] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[176],
   },
   {
     /* [998] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[178],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [999] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[180],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [1000] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[200],
+    /* matcher indices */ &kMatcherIndices[178],
   },
   {
     /* [1001] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[182],
+    /* matcher indices */ &kMatcherIndices[180],
   },
   {
     /* [1002] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[200],
   },
   {
     /* [1003] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[182],
   },
   {
     /* [1004] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[184],
+    /* matcher indices */ &kMatcherIndices[30],
   },
   {
     /* [1005] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[186],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [1006] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[184],
   },
   {
     /* [1007] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[188],
+    /* matcher indices */ &kMatcherIndices[186],
   },
   {
     /* [1008] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [1009] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[30],
+    /* matcher indices */ &kMatcherIndices[188],
   },
   {
     /* [1010] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[190],
+    /* matcher indices */ &kMatcherIndices[5],
   },
   {
     /* [1011] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[192],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [1012] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* matcher indices */ &kMatcherIndices[190],
   },
   {
     /* [1013] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [1014] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [1015] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [1016] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[140],
+    /* matcher indices */ &kMatcherIndices[60],
+  },
+  {
+    /* [1017] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[39],
+  },
+  {
+    /* [1018] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
 };
 
@@ -8189,22 +8199,22 @@
   {
     /* [22] */
     /* name */ "T",
-    /* matcher index */ 65,
+    /* matcher index */ 69,
   },
   {
     /* [23] */
     /* name */ "T",
-    /* matcher index */ 69,
+    /* matcher index */ 63,
   },
   {
     /* [24] */
     /* name */ "T",
-    /* matcher index */ 63,
+    /* matcher index */ 70,
   },
   {
     /* [25] */
     /* name */ "T",
-    /* matcher index */ 70,
+    /* matcher index */ 65,
   },
   {
     /* [26] */
@@ -8244,12 +8254,12 @@
   {
     /* [33] */
     /* name */ "T",
-    /* matcher index */ kNoMatcher,
+    /* matcher index */ 56,
   },
   {
     /* [34] */
     /* name */ "T",
-    /* matcher index */ 56,
+    /* matcher index */ kNoMatcher,
   },
   {
     /* [35] */
@@ -8341,7 +8351,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[623],
+    /* parameters */ &kParameters[627],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8365,7 +8375,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[627],
+    /* parameters */ &kParameters[631],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8389,7 +8399,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[631],
+    /* parameters */ &kParameters[635],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8402,7 +8412,7 @@
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[836],
-    /* return matcher indices */ &kMatcherIndices[114],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8413,8 +8423,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[635],
-    /* return matcher indices */ &kMatcherIndices[114],
+    /* parameters */ &kParameters[639],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8437,7 +8447,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[639],
+    /* parameters */ &kParameters[643],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8461,7 +8471,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[643],
+    /* parameters */ &kParameters[647],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8497,7 +8507,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[687],
+    /* parameters */ &kParameters[653],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8521,7 +8531,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[653],
+    /* parameters */ &kParameters[657],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8545,7 +8555,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[657],
+    /* parameters */ &kParameters[661],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8569,7 +8579,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[605],
+    /* parameters */ &kParameters[665],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8630,7 +8640,7 @@
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[3],
     /* parameters */ &kParameters[824],
-    /* return matcher indices */ &kMatcherIndices[114],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8641,7 +8651,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[823],
+    /* parameters */ &kParameters[860],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8653,8 +8663,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[299],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[295],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8665,8 +8675,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[290],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[190],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8677,8 +8687,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[230],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[160],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8689,8 +8699,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[102],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[72],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8701,8 +8711,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[371],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[307],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8713,8 +8723,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[170],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[260],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8725,8 +8735,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[379],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[323],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8737,8 +8747,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[205],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[230],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8749,7 +8759,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[387],
+    /* parameters */ &kParameters[443],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8761,7 +8771,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[215],
+    /* parameters */ &kParameters[220],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8773,7 +8783,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[240],
+    /* parameters */ &kParameters[210],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8785,7 +8795,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[96],
+    /* parameters */ &kParameters[138],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8797,7 +8807,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[3],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[407],
+    /* parameters */ &kParameters[351],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8809,7 +8819,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[285],
+    /* parameters */ &kParameters[180],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8821,8 +8831,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[534],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[507],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline, OverloadFlag::kIsDeprecated),
     /* const eval */ nullptr,
   },
@@ -8833,8 +8843,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[1019],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
@@ -8845,8 +8855,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[968],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[970],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
@@ -8857,8 +8867,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[969],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[971],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecSplat,
   },
@@ -8869,8 +8879,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[355],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[339],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitS,
   },
@@ -8881,8 +8891,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[465],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[498],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8893,8 +8903,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[462],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[501],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8905,8 +8915,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[459],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[504],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8917,8 +8927,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[783],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[785],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8929,8 +8939,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[785],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[787],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8941,8 +8951,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[787],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[789],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -8953,8 +8963,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1016],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[979],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -8965,7 +8975,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[978],
+    /* parameters */ &kParameters[980],
     /* return matcher indices */ &kMatcherIndices[146],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -8977,7 +8987,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[979],
+    /* parameters */ &kParameters[981],
     /* return matcher indices */ &kMatcherIndices[152],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -8989,7 +8999,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[980],
+    /* parameters */ &kParameters[982],
     /* return matcher indices */ &kMatcherIndices[156],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -9001,7 +9011,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[981],
+    /* parameters */ &kParameters[983],
     /* return matcher indices */ &kMatcherIndices[158],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -9013,8 +9023,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[501],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[462],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9025,8 +9035,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[498],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[465],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9037,8 +9047,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[307],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[435],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9049,8 +9059,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[295],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[303],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9061,8 +9071,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[195],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[275],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9073,8 +9083,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[495],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[468],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9085,8 +9095,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[319],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[311],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9097,8 +9107,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[489],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[471],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9109,8 +9119,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[323],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[319],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -9121,7 +9131,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[486],
+    /* parameters */ &kParameters[474],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -9133,7 +9143,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[335],
+    /* parameters */ &kParameters[327],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -9145,7 +9155,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[347],
+    /* parameters */ &kParameters[331],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -9157,7 +9167,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[165],
+    /* parameters */ &kParameters[235],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -9169,7 +9179,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[468],
+    /* parameters */ &kParameters[486],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -9181,157 +9191,109 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[395],
+    /* parameters */ &kParameters[335],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [72] */
-    /* num parameters */ 4,
-    /* num template types */ 2,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[367],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[516],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [73] */
-    /* num parameters */ 5,
-    /* num template types */ 2,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[155],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[519],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [74] */
-    /* num parameters */ 5,
-    /* num template types */ 3,
+    /* num parameters */ 4,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[265],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[347],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [75] */
-    /* num parameters */ 6,
-    /* num template types */ 3,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[144],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[522],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [76] */
-    /* num parameters */ 4,
-    /* num template types */ 2,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[351],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[525],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [77] */
-    /* num parameters */ 5,
-    /* num template types */ 3,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[200],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[528],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [78] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 4,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[531],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[359],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [79] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[343],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[489],
+    /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [80] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[339],
-    /* return matcher indices */ &kMatcherIndices[128],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [81] */
-    /* num parameters */ 5,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[175],
-    /* return matcher indices */ &kMatcherIndices[128],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [82] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[507],
-    /* return matcher indices */ &kMatcherIndices[128],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [83] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[331],
-    /* return matcher indices */ &kMatcherIndices[128],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [84] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
@@ -9343,83 +9305,35 @@
     /* const eval */ nullptr,
   },
   {
-    /* [85] */
+    /* [81] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[546],
+    /* parameters */ &kParameters[549],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [86] */
+    /* [82] */
     /* num parameters */ 4,
     /* num template types */ 2,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[415],
+    /* parameters */ &kParameters[391],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [87] */
+    /* [83] */
     /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[555],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [88] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[558],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [89] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[561],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [90] */
-    /* num parameters */ 4,
-    /* num template types */ 2,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[431],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [91] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[564],
     /* return matcher indices */ nullptr,
@@ -9427,50 +9341,146 @@
     /* const eval */ nullptr,
   },
   {
-    /* [92] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[567],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [93] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[579],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [94] */
+    /* [84] */
     /* num parameters */ 4,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[1],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[363],
-    /* return matcher indices */ nullptr,
+    /* parameters */ &kParameters[343],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [95] */
+    /* [85] */
+    /* num parameters */ 5,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[165],
+    /* return matcher indices */ &kMatcherIndices[126],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [86] */
+    /* num parameters */ 5,
+    /* num template types */ 3,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[225],
+    /* return matcher indices */ &kMatcherIndices[126],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [87] */
+    /* num parameters */ 6,
+    /* num template types */ 3,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[96],
+    /* return matcher indices */ &kMatcherIndices[126],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [88] */
+    /* num parameters */ 4,
+    /* num template types */ 2,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[355],
+    /* return matcher indices */ &kMatcherIndices[126],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [89] */
+    /* num parameters */ 5,
+    /* num template types */ 3,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[0],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[285],
+    /* return matcher indices */ &kMatcherIndices[126],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [90] */
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[585],
-    /* return matcher indices */ nullptr,
+    /* parameters */ &kParameters[447],
+    /* return matcher indices */ &kMatcherIndices[102],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [91] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[431],
+    /* return matcher indices */ &kMatcherIndices[102],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [92] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[419],
+    /* return matcher indices */ &kMatcherIndices[102],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [93] */
+    /* num parameters */ 5,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[265],
+    /* return matcher indices */ &kMatcherIndices[102],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [94] */
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[573],
+    /* return matcher indices */ &kMatcherIndices[102],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [95] */
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[2],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[407],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9481,8 +9491,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
-    /* return matcher indices */ &kMatcherIndices[110],
+    /* parameters */ &kParameters[1019],
+    /* return matcher indices */ &kMatcherIndices[104],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
@@ -9493,8 +9503,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[957],
-    /* return matcher indices */ &kMatcherIndices[110],
+    /* parameters */ &kParameters[959],
+    /* return matcher indices */ &kMatcherIndices[104],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
@@ -9505,8 +9515,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[902],
-    /* return matcher indices */ &kMatcherIndices[110],
+    /* parameters */ &kParameters[960],
+    /* return matcher indices */ &kMatcherIndices[104],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecSplat,
   },
@@ -9517,8 +9527,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[492],
-    /* return matcher indices */ &kMatcherIndices[110],
+    /* parameters */ &kParameters[495],
+    /* return matcher indices */ &kMatcherIndices[104],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitS,
   },
@@ -9529,8 +9539,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[761],
-    /* return matcher indices */ &kMatcherIndices[110],
+    /* parameters */ &kParameters[783],
+    /* return matcher indices */ &kMatcherIndices[104],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -9541,8 +9551,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[765],
-    /* return matcher indices */ &kMatcherIndices[110],
+    /* parameters */ &kParameters[727],
+    /* return matcher indices */ &kMatcherIndices[104],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitM,
   },
@@ -9553,8 +9563,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[962],
-    /* return matcher indices */ &kMatcherIndices[102],
+    /* parameters */ &kParameters[964],
+    /* return matcher indices */ &kMatcherIndices[108],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9565,8 +9575,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[963],
-    /* return matcher indices */ &kMatcherIndices[106],
+    /* parameters */ &kParameters[965],
+    /* return matcher indices */ &kMatcherIndices[110],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9577,8 +9587,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[964],
-    /* return matcher indices */ &kMatcherIndices[108],
+    /* parameters */ &kParameters[966],
+    /* return matcher indices */ &kMatcherIndices[114],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9589,8 +9599,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[965],
-    /* return matcher indices */ &kMatcherIndices[114],
+    /* parameters */ &kParameters[967],
+    /* return matcher indices */ &kMatcherIndices[116],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9601,8 +9611,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[966],
-    /* return matcher indices */ &kMatcherIndices[116],
+    /* parameters */ &kParameters[968],
+    /* return matcher indices */ &kMatcherIndices[118],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9613,7 +9623,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[817],
+    /* parameters */ &kParameters[818],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9625,7 +9635,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[816],
+    /* parameters */ &kParameters[817],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9637,7 +9647,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[815],
+    /* parameters */ &kParameters[816],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9649,7 +9659,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[814],
+    /* parameters */ &kParameters[815],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9661,7 +9671,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[813],
+    /* parameters */ &kParameters[814],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9673,7 +9683,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[812],
+    /* parameters */ &kParameters[813],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9685,7 +9695,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[811],
+    /* parameters */ &kParameters[812],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9697,7 +9707,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[810],
+    /* parameters */ &kParameters[811],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9709,7 +9719,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[809],
+    /* parameters */ &kParameters[810],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9721,7 +9731,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[808],
+    /* parameters */ &kParameters[809],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -9733,7 +9743,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[763],
+    /* parameters */ &kParameters[663],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiply,
@@ -9745,7 +9755,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[775],
+    /* parameters */ &kParameters[667],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiply,
@@ -9757,7 +9767,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[777],
+    /* parameters */ &kParameters[677],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiply,
@@ -9769,7 +9779,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[801],
+    /* parameters */ &kParameters[679],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiply,
@@ -9779,9 +9789,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[797],
+    /* parameters */ &kParameters[803],
     /* return matcher indices */ &kMatcherIndices[10],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiply,
@@ -9791,9 +9801,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[799],
+    /* parameters */ &kParameters[681],
     /* return matcher indices */ &kMatcherIndices[10],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiply,
@@ -9803,9 +9813,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[1],
-    /* parameters */ &kParameters[597],
+    /* parameters */ &kParameters[683],
     /* return matcher indices */ &kMatcherIndices[69],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiplyMatVec,
@@ -9815,9 +9825,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[1],
-    /* parameters */ &kParameters[599],
+    /* parameters */ &kParameters[685],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiplyVecMat,
@@ -9827,9 +9837,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 3,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[0],
-    /* parameters */ &kParameters[613],
+    /* parameters */ &kParameters[691],
     /* return matcher indices */ &kMatcherIndices[26],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMultiplyMatMat,
@@ -9841,7 +9851,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[27],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[112],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -9853,7 +9863,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[948],
+    /* parameters */ &kParameters[950],
     /* return matcher indices */ &kMatcherIndices[112],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -9865,7 +9875,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[949],
+    /* parameters */ &kParameters[951],
     /* return matcher indices */ &kMatcherIndices[112],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecSplat,
@@ -9877,7 +9887,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[753],
+    /* parameters */ &kParameters[781],
     /* return matcher indices */ &kMatcherIndices[112],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::VecInitS,
@@ -9889,7 +9899,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[951],
+    /* parameters */ &kParameters[953],
     /* return matcher indices */ &kMatcherIndices[132],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -9901,7 +9911,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[952],
+    /* parameters */ &kParameters[954],
     /* return matcher indices */ &kMatcherIndices[130],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -9913,8 +9923,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[953],
-    /* return matcher indices */ &kMatcherIndices[126],
+    /* parameters */ &kParameters[955],
+    /* return matcher indices */ &kMatcherIndices[128],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -9925,7 +9935,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[954],
+    /* parameters */ &kParameters[956],
     /* return matcher indices */ &kMatcherIndices[124],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -9937,7 +9947,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[955],
+    /* parameters */ &kParameters[957],
     /* return matcher indices */ &kMatcherIndices[120],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -9949,8 +9959,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[7],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[588],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[567],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9961,8 +9971,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[7],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[447],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[570],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9973,8 +9983,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[435],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[367],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9985,8 +9995,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[7],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[594],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[492],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9997,8 +10007,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[591],
-    /* return matcher indices */ &kMatcherIndices[118],
+    /* parameters */ &kParameters[588],
+    /* return matcher indices */ &kMatcherIndices[126],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10009,7 +10019,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[8],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[582],
+    /* parameters */ &kParameters[585],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -10021,7 +10031,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[359],
+    /* parameters */ &kParameters[395],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -10033,7 +10043,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[5],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[570],
+    /* parameters */ &kParameters[591],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -10045,8 +10055,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[1],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[609],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[601],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10057,8 +10067,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[375],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[439],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -10069,8 +10079,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[190],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[215],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -10081,8 +10091,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[150],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[205],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -10094,7 +10104,7 @@
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[132],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -10105,8 +10115,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[383],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[371],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -10117,8 +10127,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[210],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[155],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -10129,8 +10139,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[391],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[383],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -10141,8 +10151,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[235],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[150],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -10154,7 +10164,7 @@
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[250],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10165,8 +10175,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[72],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[78],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10177,8 +10187,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[78],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[90],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10190,7 +10200,7 @@
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[65],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10201,8 +10211,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[270],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[195],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10213,8 +10223,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[90],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[120],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10225,8 +10235,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[280],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[290],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10237,8 +10247,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[138],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[144],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10247,9 +10257,9 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[182],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -10259,9 +10269,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1001],
+    /* parameters */ &kParameters[1003],
     /* return matcher indices */ &kMatcherIndices[182],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -10271,9 +10281,9 @@
     /* num parameters */ 6,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[114],
+    /* parameters */ &kParameters[102],
     /* return matcher indices */ &kMatcherIndices[182],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitS,
@@ -10283,9 +10293,9 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[453],
+    /* parameters */ &kParameters[546],
     /* return matcher indices */ &kMatcherIndices[182],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
@@ -10297,7 +10307,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1004],
+    /* parameters */ &kParameters[1006],
     /* return matcher indices */ &kMatcherIndices[186],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10309,7 +10319,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1005],
+    /* parameters */ &kParameters[1007],
     /* return matcher indices */ &kMatcherIndices[184],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10321,9 +10331,9 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[443],
+    /* parameters */ &kParameters[363],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -10333,9 +10343,9 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[225],
+    /* parameters */ &kParameters[175],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -10345,9 +10355,9 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[255],
+    /* parameters */ &kParameters[185],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -10359,7 +10369,7 @@
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[108],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -10369,9 +10379,9 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[303],
+    /* parameters */ &kParameters[379],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -10381,163 +10391,163 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[245],
+    /* parameters */ &kParameters[200],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [172] */
+    /* num parameters */ 4,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[299],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [172] */
-    /* num parameters */ 0,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
-    /* return matcher indices */ &kMatcherIndices[176],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Zero,
-  },
-  {
     /* [173] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[995],
-    /* return matcher indices */ &kMatcherIndices[176],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
-  },
-  {
-    /* [174] */
-    /* num parameters */ 8,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[57],
-    /* return matcher indices */ &kMatcherIndices[176],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitS,
-  },
-  {
-    /* [175] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[795],
-    /* return matcher indices */ &kMatcherIndices[176],
-    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::MatInitV,
-  },
-  {
-    /* [176] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[998],
-    /* return matcher indices */ &kMatcherIndices[180],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [177] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[999],
-    /* return matcher indices */ &kMatcherIndices[178],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Conv,
-  },
-  {
-    /* [178] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[403],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [179] */
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[260],
+    /* parameters */ &kParameters[280],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [180] */
+    /* [174] */
     /* num parameters */ 5,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[275],
+    /* parameters */ &kParameters[270],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [181] */
+    /* [175] */
     /* num parameters */ 6,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[120],
+    /* parameters */ &kParameters[114],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [182] */
+    /* [176] */
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[411],
+    /* parameters */ &kParameters[315],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [183] */
+    /* [177] */
     /* num parameters */ 5,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[160],
+    /* parameters */ &kParameters[255],
     /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
+    /* [178] */
+    /* num parameters */ 0,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1019],
+    /* return matcher indices */ &kMatcherIndices[176],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Zero,
+  },
+  {
+    /* [179] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[997],
+    /* return matcher indices */ &kMatcherIndices[176],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [180] */
+    /* num parameters */ 8,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[49],
+    /* return matcher indices */ &kMatcherIndices[176],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitS,
+  },
+  {
+    /* [181] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[801],
+    /* return matcher indices */ &kMatcherIndices[176],
+    /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::MatInitV,
+  },
+  {
+    /* [182] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1000],
+    /* return matcher indices */ &kMatcherIndices[180],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
+    /* [183] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1001],
+    /* return matcher indices */ &kMatcherIndices[178],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Conv,
+  },
+  {
     /* [184] */
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[226],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -10547,9 +10557,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[935],
+    /* parameters */ &kParameters[937],
     /* return matcher indices */ &kMatcherIndices[226],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -10559,7 +10569,7 @@
     /* num parameters */ 16,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[0],
     /* return matcher indices */ &kMatcherIndices[226],
@@ -10571,9 +10581,9 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[419],
+    /* parameters */ &kParameters[415],
     /* return matcher indices */ &kMatcherIndices[226],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
@@ -10585,7 +10595,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[852],
+    /* parameters */ &kParameters[840],
     /* return matcher indices */ &kMatcherIndices[228],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10609,8 +10619,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[327],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[403],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10621,8 +10631,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[180],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[240],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10633,8 +10643,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[185],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[245],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10645,8 +10655,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[126],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[84],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10657,8 +10667,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[311],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[387],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10669,8 +10679,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[220],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[170],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10679,9 +10689,9 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[206],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -10691,9 +10701,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[956],
+    /* parameters */ &kParameters[958],
     /* return matcher indices */ &kMatcherIndices[206],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -10703,9 +10713,9 @@
     /* num parameters */ 12,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[16],
+    /* parameters */ &kParameters[28],
     /* return matcher indices */ &kMatcherIndices[206],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitS,
@@ -10715,9 +10725,9 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[315],
+    /* parameters */ &kParameters[411],
     /* return matcher indices */ &kMatcherIndices[206],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
@@ -10729,7 +10739,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[941],
+    /* parameters */ &kParameters[943],
     /* return matcher indices */ &kMatcherIndices[220],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10741,8 +10751,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[938],
-    /* return matcher indices */ &kMatcherIndices[218],
+    /* parameters */ &kParameters[940],
+    /* return matcher indices */ &kMatcherIndices[216],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -10751,9 +10761,9 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -10763,9 +10773,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[989],
+    /* parameters */ &kParameters[991],
     /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -10775,9 +10785,9 @@
     /* num parameters */ 6,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[84],
+    /* parameters */ &kParameters[126],
     /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitS,
@@ -10787,9 +10797,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[793],
+    /* parameters */ &kParameters[799],
     /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
@@ -10801,7 +10811,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[992],
+    /* parameters */ &kParameters[994],
     /* return matcher indices */ &kMatcherIndices[174],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10813,7 +10823,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[993],
+    /* parameters */ &kParameters[995],
     /* return matcher indices */ &kMatcherIndices[170],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10823,9 +10833,9 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[160],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -10835,9 +10845,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[983],
+    /* parameters */ &kParameters[985],
     /* return matcher indices */ &kMatcherIndices[160],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -10847,9 +10857,9 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[399],
+    /* parameters */ &kParameters[375],
     /* return matcher indices */ &kMatcherIndices[160],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitS,
@@ -10859,7 +10869,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[791],
     /* return matcher indices */ &kMatcherIndices[160],
@@ -10873,7 +10883,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[986],
+    /* parameters */ &kParameters[988],
     /* return matcher indices */ &kMatcherIndices[166],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10885,7 +10895,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[987],
+    /* parameters */ &kParameters[989],
     /* return matcher indices */ &kMatcherIndices[164],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10895,9 +10905,9 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[194],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -10907,9 +10917,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1013],
+    /* parameters */ &kParameters[1015],
     /* return matcher indices */ &kMatcherIndices[194],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -10919,9 +10929,9 @@
     /* num parameters */ 12,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[28],
+    /* parameters */ &kParameters[16],
     /* return matcher indices */ &kMatcherIndices[194],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitS,
@@ -10931,9 +10941,9 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[504],
+    /* parameters */ &kParameters[576],
     /* return matcher indices */ &kMatcherIndices[194],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
@@ -10945,7 +10955,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1012],
+    /* parameters */ &kParameters[1014],
     /* return matcher indices */ &kMatcherIndices[198],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10957,7 +10967,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1006],
+    /* parameters */ &kParameters[1008],
     /* return matcher indices */ &kMatcherIndices[196],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -10967,9 +10977,9 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[188],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -10979,9 +10989,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1007],
+    /* parameters */ &kParameters[1009],
     /* return matcher indices */ &kMatcherIndices[188],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -10991,7 +11001,7 @@
     /* num parameters */ 9,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[40],
     /* return matcher indices */ &kMatcherIndices[188],
@@ -11003,9 +11013,9 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[450],
+    /* parameters */ &kParameters[561],
     /* return matcher indices */ &kMatcherIndices[188],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
@@ -11017,7 +11027,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1010],
+    /* parameters */ &kParameters[1012],
     /* return matcher indices */ &kMatcherIndices[192],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -11029,7 +11039,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1011],
+    /* parameters */ &kParameters[1013],
     /* return matcher indices */ &kMatcherIndices[190],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -11039,9 +11049,9 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[200],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -11051,9 +11061,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1000],
+    /* parameters */ &kParameters[1002],
     /* return matcher indices */ &kMatcherIndices[200],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -11063,9 +11073,9 @@
     /* num parameters */ 8,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[49],
+    /* parameters */ &kParameters[57],
     /* return matcher indices */ &kMatcherIndices[200],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitS,
@@ -11075,9 +11085,9 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[439],
+    /* parameters */ &kParameters[399],
     /* return matcher indices */ &kMatcherIndices[200],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::MatInitV,
@@ -11089,7 +11099,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[982],
+    /* parameters */ &kParameters[984],
     /* return matcher indices */ &kMatcherIndices[204],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -11101,7 +11111,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[967],
+    /* parameters */ &kParameters[969],
     /* return matcher indices */ &kMatcherIndices[202],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -11113,7 +11123,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[669],
+    /* parameters */ &kParameters[619],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpPlus,
@@ -11125,7 +11135,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[671],
+    /* parameters */ &kParameters[621],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpPlus,
@@ -11137,7 +11147,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[685],
+    /* parameters */ &kParameters[625],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpPlus,
@@ -11149,7 +11159,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[695],
+    /* parameters */ &kParameters[629],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpPlus,
@@ -11159,9 +11169,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[701],
+    /* parameters */ &kParameters[641],
     /* return matcher indices */ &kMatcherIndices[10],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpPlus,
@@ -11173,7 +11183,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[745],
+    /* parameters */ &kParameters[645],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMinus,
@@ -11185,7 +11195,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[747],
+    /* parameters */ &kParameters[649],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMinus,
@@ -11197,7 +11207,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[755],
+    /* parameters */ &kParameters[651],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMinus,
@@ -11209,7 +11219,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[757],
+    /* parameters */ &kParameters[655],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMinus,
@@ -11219,9 +11229,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[759],
+    /* parameters */ &kParameters[659],
     /* return matcher indices */ &kMatcherIndices[10],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpMinus,
@@ -11233,7 +11243,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[822],
+    /* parameters */ &kParameters[823],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11245,7 +11255,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[821],
+    /* parameters */ &kParameters[822],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11257,7 +11267,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[820],
+    /* parameters */ &kParameters[821],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11269,7 +11279,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[819],
+    /* parameters */ &kParameters[820],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11281,7 +11291,7 @@
     /* num template numbers */ 2,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[818],
+    /* parameters */ &kParameters[819],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11293,8 +11303,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[679],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[721],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpOr,
   },
@@ -11305,8 +11315,8 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[681],
-    /* return matcher indices */ &kMatcherIndices[36],
+    /* parameters */ &kParameters[723],
+    /* return matcher indices */ &kMatcherIndices[39],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpOr,
   },
@@ -11317,7 +11327,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[36],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[683],
+    /* parameters */ &kParameters[725],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpOr,
@@ -11329,7 +11339,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[36],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[693],
+    /* parameters */ &kParameters[729],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpOr,
@@ -11339,9 +11349,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[725],
+    /* parameters */ &kParameters[757],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpShiftLeft,
@@ -11351,9 +11361,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[737],
+    /* parameters */ &kParameters[759],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpShiftLeft,
@@ -11365,7 +11375,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[35],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[739],
+    /* parameters */ &kParameters[771],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpShiftLeft,
@@ -11377,7 +11387,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[35],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[741],
+    /* parameters */ &kParameters[773],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpShiftLeft,
@@ -11389,7 +11399,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[619],
+    /* parameters */ &kParameters[693],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpDivide,
@@ -11401,7 +11411,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[625],
+    /* parameters */ &kParameters[695],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpDivide,
@@ -11413,7 +11423,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[647],
+    /* parameters */ &kParameters[697],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpDivide,
@@ -11425,7 +11435,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[651],
+    /* parameters */ &kParameters[699],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpDivide,
@@ -11435,9 +11445,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[655],
+    /* parameters */ &kParameters[701],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11447,9 +11457,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[659],
+    /* parameters */ &kParameters[703],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11459,9 +11469,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[661],
+    /* parameters */ &kParameters[705],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11471,9 +11481,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[663],
+    /* parameters */ &kParameters[707],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11485,8 +11495,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[667],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[713],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpAnd,
   },
@@ -11497,8 +11507,8 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[673],
-    /* return matcher indices */ &kMatcherIndices[36],
+    /* parameters */ &kParameters[715],
+    /* return matcher indices */ &kMatcherIndices[39],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpAnd,
   },
@@ -11509,7 +11519,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[36],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[675],
+    /* parameters */ &kParameters[717],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpAnd,
@@ -11521,7 +11531,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[36],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[677],
+    /* parameters */ &kParameters[719],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpAnd,
@@ -11533,8 +11543,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[1019],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
   },
@@ -11545,8 +11555,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[945],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[947],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
   },
@@ -11557,8 +11567,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[29],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[946],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[948],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
   },
@@ -11569,7 +11579,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[6],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -11581,7 +11591,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[942],
+    /* parameters */ &kParameters[944],
     /* return matcher indices */ &kMatcherIndices[6],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -11593,7 +11603,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[30],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[943],
+    /* parameters */ &kParameters[945],
     /* return matcher indices */ &kMatcherIndices[6],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -11605,7 +11615,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -11617,7 +11627,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[939],
+    /* parameters */ &kParameters[941],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -11629,7 +11639,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[31],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[940],
+    /* parameters */ &kParameters[942],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -11641,7 +11651,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[456],
+    /* parameters */ &kParameters[552],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::select_bool,
@@ -11653,7 +11663,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[549],
+    /* parameters */ &kParameters[555],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::select_bool,
@@ -11665,7 +11675,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[552],
+    /* parameters */ &kParameters[558],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::select_boolvec,
@@ -11677,7 +11687,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -11689,7 +11699,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[936],
+    /* parameters */ &kParameters[938],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -11701,7 +11711,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[32],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[937],
+    /* parameters */ &kParameters[939],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -11713,7 +11723,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
+    /* parameters */ &kParameters[1019],
     /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Zero,
@@ -11725,7 +11735,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[933],
+    /* parameters */ &kParameters[935],
     /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsInitializer, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Identity,
@@ -11735,9 +11745,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[34],
+    /* template types */ &kTemplateTypes[33],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[934],
+    /* parameters */ &kParameters[936],
     /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::Conv,
@@ -11747,7 +11757,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[477],
     /* return matcher indices */ &kMatcherIndices[1],
@@ -11759,7 +11769,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[480],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11771,7 +11781,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[483],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -11785,8 +11795,8 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[869],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[871],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpNot,
   },
@@ -11797,8 +11807,8 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[870],
-    /* return matcher indices */ &kMatcherIndices[36],
+    /* parameters */ &kParameters[872],
+    /* return matcher indices */ &kMatcherIndices[39],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpNot,
   },
@@ -11809,7 +11819,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[899],
+    /* parameters */ &kParameters[902],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -11821,7 +11831,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[898],
+    /* parameters */ &kParameters[901],
     /* return matcher indices */ &kMatcherIndices[60],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -11831,9 +11841,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[901],
+    /* parameters */ &kParameters[904],
     /* return matcher indices */ &kMatcherIndices[172],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11843,9 +11853,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[900],
+    /* parameters */ &kParameters[903],
     /* return matcher indices */ &kMatcherIndices[78],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11857,7 +11867,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[895],
+    /* parameters */ &kParameters[898],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -11869,7 +11879,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[894],
+    /* parameters */ &kParameters[897],
     /* return matcher indices */ &kMatcherIndices[60],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -11879,33 +11889,33 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[423],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::insertBits,
   },
   {
     /* [297] */
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[427],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::insertBits,
   },
   {
     /* [298] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[893],
+    /* parameters */ &kParameters[896],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11915,9 +11925,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[892],
+    /* parameters */ &kParameters[895],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11927,9 +11937,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[607],
+    /* parameters */ &kParameters[689],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11939,9 +11949,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[629],
+    /* parameters */ &kParameters[687],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11951,9 +11961,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[891],
+    /* parameters */ &kParameters[894],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11963,9 +11973,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[890],
+    /* parameters */ &kParameters[893],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11975,9 +11985,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[889],
+    /* parameters */ &kParameters[892],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11987,9 +11997,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[888],
+    /* parameters */ &kParameters[891],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -11999,9 +12009,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[887],
+    /* parameters */ &kParameters[890],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12011,9 +12021,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[886],
+    /* parameters */ &kParameters[889],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12023,9 +12033,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[773],
+    /* parameters */ &kParameters[675],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12035,9 +12045,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[771],
+    /* parameters */ &kParameters[673],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12047,9 +12057,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[769],
+    /* parameters */ &kParameters[671],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12059,9 +12069,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[767],
+    /* parameters */ &kParameters[669],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12071,9 +12081,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[904],
+    /* parameters */ &kParameters[906],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12083,9 +12093,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[903],
+    /* parameters */ &kParameters[905],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12095,9 +12105,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[885],
+    /* parameters */ &kParameters[888],
     /* return matcher indices */ &kMatcherIndices[134],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12107,9 +12117,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[884],
+    /* parameters */ &kParameters[887],
     /* return matcher indices */ &kMatcherIndices[96],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12119,9 +12129,9 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[519],
+    /* parameters */ &kParameters[453],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12131,9 +12141,9 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[522],
+    /* parameters */ &kParameters[450],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12141,59 +12151,59 @@
   {
     /* [318] */
     /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[908],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::floor,
+  },
+  {
+    /* [319] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[907],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::floor,
+  },
+  {
+    /* [320] */
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[897],
+    /* parameters */ &kParameters[900],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [319] */
+    /* [321] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[896],
+    /* parameters */ &kParameters[899],
     /* return matcher indices */ &kMatcherIndices[60],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [320] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[906],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [321] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[905],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
     /* [322] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[910],
+    /* parameters */ &kParameters[912],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::firstLeadingBit,
@@ -12203,9 +12213,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[909],
+    /* parameters */ &kParameters[911],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::firstLeadingBit,
@@ -12215,33 +12225,33 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[510],
+    /* parameters */ &kParameters[594],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::extractBits,
   },
   {
     /* [325] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[513],
+    /* parameters */ &kParameters[459],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::extractBits,
   },
   {
     /* [326] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[912],
+    /* parameters */ &kParameters[914],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12251,9 +12261,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[911],
+    /* parameters */ &kParameters[913],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12263,9 +12273,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[691],
+    /* parameters */ &kParameters[637],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12275,9 +12285,9 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[689],
+    /* parameters */ &kParameters[633],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12285,35 +12295,35 @@
   {
     /* [330] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[877],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* parameters */ &kParameters[880],
+    /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::quantizeToF16,
   },
   {
     /* [331] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[876],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* parameters */ &kParameters[879],
+    /* return matcher indices */ &kMatcherIndices[60],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::quantizeToF16,
   },
   {
     /* [332] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[875],
+    /* parameters */ &kParameters[878],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12323,9 +12333,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[913],
+    /* parameters */ &kParameters[877],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12333,33 +12343,81 @@
   {
     /* [334] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[915],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[916],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [335] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[914],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[915],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [336] */
     /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[918],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [337] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[917],
+    /* return matcher indices */ &kMatcherIndices[60],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [338] */
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[869],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::reverseBits,
+  },
+  {
+    /* [339] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[868],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::reverseBits,
+  },
+  {
+    /* [340] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[867],
     /* return matcher indices */ &kMatcherIndices[1],
@@ -12367,11 +12425,11 @@
     /* const eval */ nullptr,
   },
   {
-    /* [337] */
+    /* [341] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[866],
     /* return matcher indices */ &kMatcherIndices[30],
@@ -12379,7 +12437,7 @@
     /* const eval */ nullptr,
   },
   {
-    /* [338] */
+    /* [342] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
@@ -12388,10 +12446,10 @@
     /* parameters */ &kParameters[865],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::saturate,
   },
   {
-    /* [339] */
+    /* [343] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
@@ -12400,91 +12458,43 @@
     /* parameters */ &kParameters[864],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [340] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[863],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::saturate,
   },
   {
-    /* [341] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[862],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::saturate,
-  },
-  {
-    /* [342] */
+    /* [344] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[917],
+    /* parameters */ &kParameters[921],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [343] */
+    /* [345] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[916],
+    /* parameters */ &kParameters[920],
     /* return matcher indices */ &kMatcherIndices[60],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [344] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[861],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::sign,
-  },
-  {
-    /* [345] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[860],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::sign,
-  },
-  {
     /* [346] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[859],
+    /* parameters */ &kParameters[863],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::sign,
   },
   {
     /* [347] */
@@ -12493,19 +12503,19 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[858],
+    /* parameters */ &kParameters[862],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::sign,
   },
   {
     /* [348] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[856],
+    /* parameters */ &kParameters[861],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12515,117 +12525,117 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[855],
+    /* parameters */ &kParameters[858],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [350] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[573],
+    /* parameters */ &kParameters[857],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [351] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[576],
+    /* parameters */ &kParameters[856],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [352] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[854],
+    /* parameters */ &kParameters[579],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [353] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[853],
+    /* parameters */ &kParameters[582],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [354] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[855],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [355] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[854],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [356] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[603],
+    /* parameters */ &kParameters[599],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::step,
   },
   {
-    /* [355] */
+    /* [357] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[601],
+    /* parameters */ &kParameters[597],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::step,
   },
   {
-    /* [356] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[920],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [357] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[918],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
-    /* const eval */ nullptr,
-  },
-  {
     /* [358] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[922],
+    /* parameters */ &kParameters[923],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -12637,7 +12647,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[921],
+    /* parameters */ &kParameters[1016],
     /* return matcher indices */ &kMatcherIndices[60],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -12647,9 +12657,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[849],
+    /* parameters */ &kParameters[852],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12659,9 +12669,9 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[848],
+    /* parameters */ &kParameters[851],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12669,73 +12679,73 @@
   {
     /* [362] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[924],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[850],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [363] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[923],
-    /* return matcher indices */ &kMatcherIndices[60],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[849],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [364] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[846],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[925],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [365] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[845],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[924],
+    /* return matcher indices */ &kMatcherIndices[60],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [366] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[749],
+    /* parameters */ &kParameters[847],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [367] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[751],
+    /* parameters */ &kParameters[846],
     /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12745,7 +12755,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[926],
+    /* parameters */ &kParameters[927],
     /* return matcher indices */ &kMatcherIndices[62],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -12757,7 +12767,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[925],
+    /* parameters */ &kParameters[926],
     /* return matcher indices */ &kMatcherIndices[60],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
@@ -12767,48 +12777,48 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[721],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[775],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpGreaterThanEqual,
+    /* const eval */ nullptr,
   },
   {
     /* [371] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[723],
-    /* return matcher indices */ &kMatcherIndices[36],
+    /* parameters */ &kParameters[779],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpGreaterThanEqual,
+    /* const eval */ nullptr,
   },
   {
     /* [372] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[717],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpLessThanEqual,
+    /* parameters */ &kParameters[929],
+    /* return matcher indices */ &kMatcherIndices[62],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [373] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[719],
-    /* return matcher indices */ &kMatcherIndices[36],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpLessThanEqual,
+    /* parameters */ &kParameters[928],
+    /* return matcher indices */ &kMatcherIndices[60],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [374] */
@@ -12817,22 +12827,22 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[713],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[753],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpGreaterThan,
+    /* const eval */ &ConstEval::OpGreaterThanEqual,
   },
   {
     /* [375] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[28],
+    /* template types */ &kTemplateTypes[25],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[715],
-    /* return matcher indices */ &kMatcherIndices[36],
+    /* parameters */ &kParameters[755],
+    /* return matcher indices */ &kMatcherIndices[39],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpGreaterThan,
+    /* const eval */ &ConstEval::OpGreaterThanEqual,
   },
   {
     /* [376] */
@@ -12841,10 +12851,10 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[711],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[749],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpLessThan,
+    /* const eval */ &ConstEval::OpLessThanEqual,
   },
   {
     /* [377] */
@@ -12853,56 +12863,56 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[789],
-    /* return matcher indices */ &kMatcherIndices[36],
+    /* parameters */ &kParameters[751],
+    /* return matcher indices */ &kMatcherIndices[39],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpLessThan,
+    /* const eval */ &ConstEval::OpLessThanEqual,
   },
   {
     /* [378] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[735],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* parameters */ &kParameters[745],
+    /* return matcher indices */ &kMatcherIndices[41],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpGreaterThan,
   },
   {
     /* [379] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[733],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* parameters */ &kParameters[747],
+    /* return matcher indices */ &kMatcherIndices[39],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpGreaterThan,
   },
   {
     /* [380] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[929],
+    /* parameters */ &kParameters[769],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [381] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[928],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* parameters */ &kParameters[767],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12913,7 +12923,7 @@
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1015],
+    /* parameters */ &kParameters[932],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12925,7 +12935,7 @@
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[1009],
+    /* parameters */ &kParameters[931],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12935,63 +12945,87 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[931],
+    /* parameters */ &kParameters[1018],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countTrailingZeros,
+    /* const eval */ &ConstEval::abs,
   },
   {
     /* [385] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[930],
+    /* parameters */ &kParameters[853],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countTrailingZeros,
+    /* const eval */ &ConstEval::abs,
   },
   {
     /* [386] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[944],
+    /* parameters */ &kParameters[952],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countOneBits,
+    /* const eval */ &ConstEval::countTrailingZeros,
   },
   {
     /* [387] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[932],
+    /* parameters */ &kParameters[933],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countOneBits,
+    /* const eval */ &ConstEval::countTrailingZeros,
   },
   {
     /* [388] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[962],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::countOneBits,
+  },
+  {
+    /* [389] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[961],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::countOneBits,
+  },
+  {
+    /* [390] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[988],
+    /* parameters */ &kParameters[808],
     /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [389] */
+    /* [391] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
@@ -13003,85 +13037,61 @@
     /* const eval */ nullptr,
   },
   {
-    /* [390] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[950],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countLeadingZeros,
-  },
-  {
-    /* [391] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[947],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::countLeadingZeros,
-  },
-  {
     /* [392] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[959],
+    /* parameters */ &kParameters[972],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::countLeadingZeros,
   },
   {
     /* [393] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[958],
+    /* parameters */ &kParameters[963],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::countLeadingZeros,
   },
   {
     /* [394] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[707],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[741],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpNotEqual,
+    /* const eval */ &ConstEval::OpLessThan,
   },
   {
     /* [395] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[709],
-    /* return matcher indices */ &kMatcherIndices[36],
+    /* parameters */ &kParameters[743],
+    /* return matcher indices */ &kMatcherIndices[39],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpNotEqual,
+    /* const eval */ &ConstEval::OpLessThan,
   },
   {
     /* [396] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[961],
+    /* parameters */ &kParameters[974],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -13091,447 +13101,687 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[960],
+    /* parameters */ &kParameters[973],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [398] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[28],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[471],
+    /* parameters */ &kParameters[976],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::clamp,
+    /* const eval */ nullptr,
   },
   {
     /* [399] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[975],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [400] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[28],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[540],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::clamp,
+  },
+  {
+    /* [401] */
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[28],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[474],
+    /* parameters */ &kParameters[537],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::clamp,
   },
   {
-    /* [400] */
+    /* [402] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[971],
+    /* parameters */ &kParameters[978],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [401] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[970],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [402] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[537],
-    /* return matcher indices */ &kMatcherIndices[128],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::ceil,
   },
   {
     /* [403] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[977],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::ceil,
+  },
+  {
+    /* [404] */
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[540],
-    /* return matcher indices */ &kMatcherIndices[128],
+    /* parameters */ &kParameters[510],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [404] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[973],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atanh,
-  },
-  {
     /* [405] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[972],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[513],
+    /* return matcher indices */ &kMatcherIndices[102],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atanh,
+    /* const eval */ nullptr,
   },
   {
     /* [406] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[781],
+    /* parameters */ &kParameters[987],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atan2,
+    /* const eval */ &ConstEval::atanh,
   },
   {
     /* [407] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[779],
+    /* parameters */ &kParameters[986],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atan2,
+    /* const eval */ &ConstEval::atanh,
   },
   {
     /* [408] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[26],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[703],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpEqual,
+    /* parameters */ &kParameters[797],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::atan2,
   },
   {
     /* [409] */
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[793],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::atan2,
+  },
+  {
+    /* [410] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[739],
+    /* return matcher indices */ &kMatcherIndices[41],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpNotEqual,
+  },
+  {
+    /* [411] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[705],
-    /* return matcher indices */ &kMatcherIndices[36],
+    /* parameters */ &kParameters[805],
+    /* return matcher indices */ &kMatcherIndices[39],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpNotEqual,
+  },
+  {
+    /* [412] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[26],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[735],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::OpEqual,
   },
   {
-    /* [410] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[975],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atan,
-  },
-  {
-    /* [411] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[974],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::atan,
-  },
-  {
-    /* [412] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[977],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::asinh,
-  },
-  {
     /* [413] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[26],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[976],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::asinh,
+    /* parameters */ &kParameters[737],
+    /* return matcher indices */ &kMatcherIndices[39],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpEqual,
   },
   {
     /* [414] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[36],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[805],
+    /* parameters */ &kParameters[993],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpXor,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::atan,
   },
   {
     /* [415] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[36],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[665],
+    /* parameters */ &kParameters[992],
     /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpXor,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::atan,
   },
   {
     /* [416] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[985],
+    /* parameters */ &kParameters[999],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::asin,
+    /* const eval */ &ConstEval::asinh,
   },
   {
     /* [417] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[24],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[984],
+    /* parameters */ &kParameters[998],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::asinh,
+  },
+  {
+    /* [418] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[36],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[709],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpXor,
+  },
+  {
+    /* [419] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[36],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[711],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpXor,
+  },
+  {
+    /* [420] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1005],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::asin,
+  },
+  {
+    /* [421] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[1004],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::asin,
   },
   {
-    /* [418] */
+    /* [422] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[991],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[922],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::any,
   },
   {
-    /* [419] */
+    /* [423] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[990],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[1011],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::any,
   },
   {
-    /* [420] */
+    /* [424] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[996],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::all,
   },
   {
-    /* [421] */
+    /* [425] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[994],
-    /* return matcher indices */ &kMatcherIndices[38],
+    /* parameters */ &kParameters[1017],
+    /* return matcher indices */ &kMatcherIndices[41],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [422] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1002],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [423] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[997],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [424] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1008],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [425] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[1003],
-    /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::all,
   },
   {
     /* [426] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[873],
+    /* parameters */ &kParameters[949],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpUnaryMinus,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [427] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[37],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[874],
+    /* parameters */ &kParameters[990],
     /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpUnaryMinus,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
   {
     /* [428] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[36],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[871],
+    /* parameters */ &kParameters[934],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpComplement,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::acos,
   },
   {
     /* [429] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[36],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[872],
+    /* parameters */ &kParameters[946],
     /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::OpComplement,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::acos,
   },
   {
     /* [430] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[851],
+    /* parameters */ &kParameters[875],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpUnaryMinus,
   },
   {
     /* [431] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[37],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[850],
+    /* parameters */ &kParameters[876],
     /* return matcher indices */ &kMatcherIndices[30],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpUnaryMinus,
   },
   {
     /* [432] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[36],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[908],
+    /* parameters */ &kParameters[873],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::firstTrailingBit,
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpComplement,
   },
   {
     /* [433] */
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
+    /* template types */ &kTemplateTypes[36],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[907],
+    /* parameters */ &kParameters[874],
+    /* return matcher indices */ &kMatcherIndices[30],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::OpComplement,
+  },
+  {
+    /* [434] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[910],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::firstTrailingBit,
+  },
+  {
+    /* [435] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[909],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ &ConstEval::firstTrailingBit,
   },
   {
-    /* [434] */
+    /* [436] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[34],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[870],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ &ConstEval::Identity,
+  },
+  {
+    /* [437] */
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[534],
+    /* return matcher indices */ &kMatcherIndices[208],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [438] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[615],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [439] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[613],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [440] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[611],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [441] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[623],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [442] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[609],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [443] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[607],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [444] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[605],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [445] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[603],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [446] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[731],
+    /* return matcher indices */ &kMatcherIndices[41],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [447] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[733],
+    /* return matcher indices */ &kMatcherIndices[41],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [448] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[795],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [449] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[859],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [450] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1019],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [451] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1019],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [452] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[841],
+    /* return matcher indices */ &kMatcherIndices[102],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [453] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[842],
+    /* return matcher indices */ &kMatcherIndices[102],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [454] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
@@ -13543,223 +13793,7 @@
     /* const eval */ nullptr,
   },
   {
-    /* [435] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[649],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [436] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[645],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [437] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[641],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [438] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[637],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [439] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[633],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [440] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[621],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [441] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[617],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [442] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[615],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [443] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[611],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [444] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[697],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [445] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[699],
-    /* return matcher indices */ &kMatcherIndices[38],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [446] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[857],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [447] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[880],
-    /* return matcher indices */ &kMatcherIndices[95],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [448] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [449] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[840],
-    /* return matcher indices */ &kMatcherIndices[128],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [450] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[841],
-    /* return matcher indices */ &kMatcherIndices[128],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [451] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[842],
-    /* return matcher indices */ &kMatcherIndices[132],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [452] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[25],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[528],
-    /* return matcher indices */ &kMatcherIndices[210],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [453] */
+    /* [455] */
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
@@ -13771,74 +13805,50 @@
     /* const eval */ nullptr,
   },
   {
-    /* [454] */
+    /* [456] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[5],
-    /* parameters */ &kParameters[847],
-    /* return matcher indices */ &kMatcherIndices[22],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [455] */
-    /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[1017],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [456] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[525],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* parameters */ &kParameters[845],
+    /* return matcher indices */ &kMatcherIndices[132],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [457] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[803],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[5],
+    /* parameters */ &kParameters[848],
+    /* return matcher indices */ &kMatcherIndices[22],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [458] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[878],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[531],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [459] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[879],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[617],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -13869,35 +13879,35 @@
   {
     /* [462] */
     /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[883],
-    /* return matcher indices */ &kMatcherIndices[30],
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[884],
+    /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [463] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[38],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[743],
-    /* return matcher indices */ &kMatcherIndices[110],
+    /* parameters */ &kParameters[885],
+    /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [464] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[516],
+    /* parameters */ &kParameters[886],
     /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -13905,60 +13915,60 @@
   {
     /* [465] */
     /* num parameters */ 2,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
+    /* template types */ &kTemplateTypes[22],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[727],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* parameters */ &kParameters[777],
+    /* return matcher indices */ &kMatcherIndices[104],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [466] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[38],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[729],
-    /* return matcher indices */ &kMatcherIndices[4],
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[456],
+    /* return matcher indices */ &kMatcherIndices[30],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [467] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[22],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[731],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[761],
+    /* return matcher indices */ &kMatcherIndices[95],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [468] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[23],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[927],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[763],
+    /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [469] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[33],
-    /* template numbers */ &kTemplateNumbers[8],
-    /* parameters */ &kParameters[1014],
-    /* return matcher indices */ &kMatcherIndices[95],
+    /* template types */ &kTemplateTypes[25],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[765],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -13966,234 +13976,258 @@
     /* [470] */
     /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[33],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[868],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[930],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ &ConstEval::Identity,
+    /* const eval */ nullptr,
+  },
+  {
+    /* [471] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[34],
+    /* template numbers */ &kTemplateNumbers[8],
+    /* parameters */ &kParameters[1010],
+    /* return matcher indices */ &kMatcherIndices[95],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [472] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[38],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[883],
+    /* return matcher indices */ &kMatcherIndices[95],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
   },
 };
 
 constexpr IntrinsicInfo kBuiltins[] = {
   {
     /* [0] */
-    /* fn abs<T : fiu32_f16>(T) -> T */
-    /* fn abs<N : num, T : fiu32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn abs<T : fia_fiu32_f16>(T) -> T */
+    /* fn abs<N : num, T : fia_fiu32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[382],
+    /* overloads */ &kOverloads[384],
   },
   {
     /* [1] */
-    /* fn acos<T : f32_f16>(T) -> T */
-    /* fn acos<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn acos<T : fa_f32_f16>(@test_value(0.87758256189) T) -> T */
+    /* fn acos<N : num, T : fa_f32_f16>(@test_value(0.87758256189) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[424],
+    /* overloads */ &kOverloads[428],
   },
   {
     /* [2] */
     /* fn acosh<T : f32_f16>(T) -> T */
     /* fn acosh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[422],
+    /* overloads */ &kOverloads[426],
   },
   {
     /* [3] */
     /* fn all(bool) -> bool */
     /* fn all<N : num>(vec<N, bool>) -> bool */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[420],
+    /* overloads */ &kOverloads[424],
   },
   {
     /* [4] */
     /* fn any(bool) -> bool */
     /* fn any<N : num>(vec<N, bool>) -> bool */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[418],
+    /* overloads */ &kOverloads[422],
   },
   {
     /* [5] */
     /* fn arrayLength<T, A : access>(ptr<storage, array<T>, A>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[469],
+    /* overloads */ &kOverloads[471],
   },
   {
     /* [6] */
     /* fn asin<T : fa_f32_f16>(@test_value(0.479425538604) T) -> T */
     /* fn asin<N : num, T : fa_f32_f16>(@test_value(0.479425538604) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[416],
+    /* overloads */ &kOverloads[420],
   },
   {
     /* [7] */
     /* fn asinh<T : fa_f32_f16>(T) -> T */
     /* fn asinh<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[412],
+    /* overloads */ &kOverloads[416],
   },
   {
     /* [8] */
     /* fn atan<T : fa_f32_f16>(T) -> T */
     /* fn atan<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[410],
+    /* overloads */ &kOverloads[414],
   },
   {
     /* [9] */
     /* fn atan2<T : fa_f32_f16>(T, T) -> T */
     /* fn atan2<T : fa_f32_f16, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[406],
+    /* overloads */ &kOverloads[408],
   },
   {
     /* [10] */
     /* fn atanh<T : fa_f32_f16>(@test_value(0.5) T) -> T */
     /* fn atanh<N : num, T : fa_f32_f16>(@test_value(0.5) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[404],
+    /* overloads */ &kOverloads[406],
   },
   {
     /* [11] */
-    /* fn ceil<T : f32_f16>(T) -> T */
-    /* fn ceil<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn ceil<T : fa_f32_f16>(@test_value(1.5) T) -> T */
+    /* fn ceil<N : num, T : fa_f32_f16>(@test_value(1.5) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[400],
+    /* overloads */ &kOverloads[402],
   },
   {
     /* [12] */
     /* fn clamp<T : fia_fiu32_f16>(T, T, T) -> T */
     /* fn clamp<T : fia_fiu32_f16, N : num>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[398],
+    /* overloads */ &kOverloads[400],
   },
   {
     /* [13] */
     /* fn cos<T : f32_f16>(T) -> T */
     /* fn cos<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[396],
+    /* overloads */ &kOverloads[398],
   },
   {
     /* [14] */
     /* fn cosh<T : f32_f16>(T) -> T */
     /* fn cosh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[392],
+    /* overloads */ &kOverloads[396],
   },
   {
     /* [15] */
     /* fn countLeadingZeros<T : iu32>(T) -> T */
     /* fn countLeadingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[390],
+    /* overloads */ &kOverloads[392],
   },
   {
     /* [16] */
     /* fn countOneBits<T : iu32>(T) -> T */
     /* fn countOneBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[386],
+    /* overloads */ &kOverloads[388],
   },
   {
     /* [17] */
     /* fn countTrailingZeros<T : iu32>(T) -> T */
     /* fn countTrailingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[384],
+    /* overloads */ &kOverloads[386],
   },
   {
     /* [18] */
     /* fn cross<T : f32_f16>(vec3<T>, vec3<T>) -> vec3<T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[463],
+    /* overloads */ &kOverloads[465],
   },
   {
     /* [19] */
     /* fn degrees<T : f32_f16>(T) -> T */
     /* fn degrees<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[380],
+    /* overloads */ &kOverloads[382],
   },
   {
     /* [20] */
     /* fn determinant<N : num, T : f32_f16>(mat<N, N, T>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[468],
+    /* overloads */ &kOverloads[470],
   },
   {
     /* [21] */
     /* fn distance<T : f32_f16>(T, T) -> T */
     /* fn distance<N : num, T : f32_f16>(vec<N, T>, vec<N, T>) -> T */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[378],
+    /* overloads */ &kOverloads[380],
   },
   {
     /* [22] */
     /* fn dot<N : num, T : fiu32_f16>(vec<N, T>, vec<N, T>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[467],
+    /* overloads */ &kOverloads[469],
   },
   {
     /* [23] */
     /* fn dot4I8Packed(u32, u32) -> i32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[466],
+    /* overloads */ &kOverloads[468],
   },
   {
     /* [24] */
     /* fn dot4U8Packed(u32, u32) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[465],
+    /* overloads */ &kOverloads[467],
   },
   {
     /* [25] */
     /* fn dpdx(f32) -> f32 */
     /* fn dpdx<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[368],
+    /* overloads */ &kOverloads[372],
   },
   {
     /* [26] */
     /* fn dpdxCoarse(f32) -> f32 */
     /* fn dpdxCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[362],
+    /* overloads */ &kOverloads[368],
   },
   {
     /* [27] */
     /* fn dpdxFine(f32) -> f32 */
     /* fn dpdxFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[358],
+    /* overloads */ &kOverloads[364],
   },
   {
     /* [28] */
     /* fn dpdy(f32) -> f32 */
     /* fn dpdy<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[356],
+    /* overloads */ &kOverloads[358],
   },
   {
     /* [29] */
     /* fn dpdyCoarse(f32) -> f32 */
     /* fn dpdyCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[342],
+    /* overloads */ &kOverloads[344],
   },
   {
     /* [30] */
     /* fn dpdyFine(f32) -> f32 */
     /* fn dpdyFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[334],
+    /* overloads */ &kOverloads[336],
   },
   {
     /* [31] */
     /* fn exp<T : f32_f16>(T) -> T */
     /* fn exp<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[332],
+    /* overloads */ &kOverloads[334],
   },
   {
     /* [32] */
@@ -14213,7 +14247,7 @@
     /* [34] */
     /* fn faceForward<N : num, T : f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[464],
+    /* overloads */ &kOverloads[466],
   },
   {
     /* [35] */
@@ -14227,14 +14261,14 @@
     /* fn firstTrailingBit<T : iu32>(T) -> T */
     /* fn firstTrailingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[432],
+    /* overloads */ &kOverloads[434],
   },
   {
     /* [37] */
-    /* fn floor<T : f32_f16>(T) -> T */
-    /* fn floor<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn floor<T : fa_f32_f16>(@test_value(1.5) T) -> T */
+    /* fn floor<N : num, T : fa_f32_f16>(@test_value(1.5) vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[320],
+    /* overloads */ &kOverloads[318],
   },
   {
     /* [38] */
@@ -14269,7 +14303,7 @@
     /* fn fwidthCoarse(f32) -> f32 */
     /* fn fwidthCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[318],
+    /* overloads */ &kOverloads[320],
   },
   {
     /* [43] */
@@ -14353,37 +14387,37 @@
     /* [54] */
     /* fn normalize<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[462],
+    /* overloads */ &kOverloads[464],
   },
   {
     /* [55] */
     /* fn pack2x16float(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[461],
+    /* overloads */ &kOverloads[463],
   },
   {
     /* [56] */
     /* fn pack2x16snorm(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[460],
+    /* overloads */ &kOverloads[462],
   },
   {
     /* [57] */
     /* fn pack2x16unorm(vec2<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[447],
+    /* overloads */ &kOverloads[472],
   },
   {
     /* [58] */
     /* fn pack4x8snorm(vec4<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[459],
+    /* overloads */ &kOverloads[461],
   },
   {
     /* [59] */
     /* fn pack4x8unorm(vec4<f32>) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[458],
+    /* overloads */ &kOverloads[460],
   },
   {
     /* [60] */
@@ -14394,46 +14428,53 @@
   },
   {
     /* [61] */
-    /* fn radians<T : f32_f16>(T) -> T */
-    /* fn radians<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn quantizeToF16(f32) -> f32 */
+    /* fn quantizeToF16<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[330],
   },
   {
     /* [62] */
-    /* fn reflect<N : num, T : f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[457],
+    /* fn radians<T : f32_f16>(T) -> T */
+    /* fn radians<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[332],
   },
   {
     /* [63] */
-    /* fn refract<N : num, T : f32_f16>(vec<N, T>, vec<N, T>, T) -> vec<N, T> */
+    /* fn reflect<N : num, T : f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[456],
+    /* overloads */ &kOverloads[459],
   },
   {
     /* [64] */
-    /* fn reverseBits<T : iu32>(T) -> T */
-    /* fn reverseBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[336],
+    /* fn refract<N : num, T : f32_f16>(vec<N, T>, vec<N, T>, T) -> vec<N, T> */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[458],
   },
   {
     /* [65] */
-    /* fn round<T : f32_f16>(T) -> T */
-    /* fn round<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn reverseBits<T : iu32>(T) -> T */
+    /* fn reverseBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[338],
   },
   {
     /* [66] */
-    /* fn saturate<T : fa_f32_f16>(@test_value(2) T) -> T */
-    /* fn saturate<T : fa_f32_f16, N : num>(@test_value(2) vec<N, T>) -> vec<N, T> */
+    /* fn round<T : f32_f16>(T) -> T */
+    /* fn round<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[340],
   },
   {
     /* [67] */
+    /* fn saturate<T : fa_f32_f16>(@test_value(2) T) -> T */
+    /* fn saturate<T : fa_f32_f16, N : num>(@test_value(2) vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[342],
+  },
+  {
+    /* [68] */
     /* fn select<T : scalar>(T, T, bool) -> T */
     /* fn select<T : scalar, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
     /* fn select<N : num, T : scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
@@ -14441,118 +14482,118 @@
     /* overloads */ &kOverloads[276],
   },
   {
-    /* [68] */
+    /* [69] */
     /* fn sign<T : fa_f32_f16>(T) -> T */
     /* fn sign<N : num, T : fa_f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[344],
-  },
-  {
-    /* [69] */
-    /* fn sin<T : f32_f16>(T) -> T */
-    /* fn sin<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
     /* overloads */ &kOverloads[346],
   },
   {
     /* [70] */
-    /* fn sinh<T : f32_f16>(T) -> T */
-    /* fn sinh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn sin<T : f32_f16>(T) -> T */
+    /* fn sin<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[348],
   },
   {
     /* [71] */
-    /* fn smoothstep<T : f32_f16>(T, T, T) -> T */
-    /* fn smoothstep<N : num, T : f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* fn sinh<T : f32_f16>(T) -> T */
+    /* fn sinh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[350],
   },
   {
     /* [72] */
-    /* fn sqrt<T : f32_f16>(T) -> T */
-    /* fn sqrt<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn smoothstep<T : f32_f16>(T, T, T) -> T */
+    /* fn smoothstep<N : num, T : f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[352],
   },
   {
     /* [73] */
-    /* fn step<T : fa_f32_f16>(T, T) -> T */
-    /* fn step<N : num, T : fa_f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* fn sqrt<T : f32_f16>(T) -> T */
+    /* fn sqrt<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[354],
   },
   {
     /* [74] */
-    /* fn storageBarrier() */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[455],
+    /* fn step<T : fa_f32_f16>(T, T) -> T */
+    /* fn step<N : num, T : fa_f32_f16>(vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[356],
   },
   {
     /* [75] */
-    /* fn tan<T : f32_f16>(T) -> T */
-    /* fn tan<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[430],
+    /* fn storageBarrier() */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[450],
   },
   {
     /* [76] */
-    /* fn tanh<T : f32_f16>(T) -> T */
-    /* fn tanh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* fn tan<T : f32_f16>(T) -> T */
+    /* fn tan<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[360],
   },
   {
     /* [77] */
+    /* fn tanh<T : f32_f16>(T) -> T */
+    /* fn tanh<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[362],
+  },
+  {
+    /* [78] */
     /* fn transpose<M : num, N : num, T : f32_f16>(mat<M, N, T>) -> mat<N, M, T> */
     /* num overloads */ 1,
+    /* overloads */ &kOverloads[457],
+  },
+  {
+    /* [79] */
+    /* fn trunc<T : f32_f16>(T) -> T */
+    /* fn trunc<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[366],
+  },
+  {
+    /* [80] */
+    /* fn unpack2x16float(u32) -> vec2<f32> */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[456],
+  },
+  {
+    /* [81] */
+    /* fn unpack2x16snorm(u32) -> vec2<f32> */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[455],
+  },
+  {
+    /* [82] */
+    /* fn unpack2x16unorm(u32) -> vec2<f32> */
+    /* num overloads */ 1,
     /* overloads */ &kOverloads[454],
   },
   {
-    /* [78] */
-    /* fn trunc<T : f32_f16>(T) -> T */
-    /* fn trunc<N : num, T : f32_f16>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[364],
-  },
-  {
-    /* [79] */
-    /* fn unpack2x16float(u32) -> vec2<f32> */
+    /* [83] */
+    /* fn unpack4x8snorm(u32) -> vec4<f32> */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[453],
   },
   {
-    /* [80] */
-    /* fn unpack2x16snorm(u32) -> vec2<f32> */
+    /* [84] */
+    /* fn unpack4x8unorm(u32) -> vec4<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[434],
+    /* overloads */ &kOverloads[452],
   },
   {
-    /* [81] */
-    /* fn unpack2x16unorm(u32) -> vec2<f32> */
+    /* [85] */
+    /* fn workgroupBarrier() */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[451],
   },
   {
-    /* [82] */
-    /* fn unpack4x8snorm(u32) -> vec4<f32> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[450],
-  },
-  {
-    /* [83] */
-    /* fn unpack4x8unorm(u32) -> vec4<f32> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[449],
-  },
-  {
-    /* [84] */
-    /* fn workgroupBarrier() */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[448],
-  },
-  {
-    /* [85] */
+    /* [86] */
     /* fn textureDimensions<T : fiu32>(texture: texture_1d<T>) -> u32 */
     /* fn textureDimensions<T : fiu32, L : iu32>(texture: texture_1d<T>, level: L) -> u32 */
     /* fn textureDimensions<T : fiu32>(texture: texture_2d<T>) -> vec2<u32> */
@@ -14584,7 +14625,7 @@
     /* overloads */ &kOverloads[0],
   },
   {
-    /* [86] */
+    /* [87] */
     /* fn textureGather<T : fiu32, C : iu32>(@const component: C, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>) -> vec4<T> */
     /* fn textureGather<T : fiu32, C : iu32>(@const component: C, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>, @const offset: vec2<i32>) -> vec4<T> */
     /* fn textureGather<T : fiu32, C : iu32, A : iu32>(@const component: C, texture: texture_2d_array<T>, sampler: sampler, coords: vec2<f32>, array_index: A) -> vec4<T> */
@@ -14598,10 +14639,10 @@
     /* fn textureGather(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>) -> vec4<f32> */
     /* fn textureGather<A : iu32>(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: A) -> vec4<f32> */
     /* num overloads */ 12,
-    /* overloads */ &kOverloads[72],
+    /* overloads */ &kOverloads[84],
   },
   {
-    /* [87] */
+    /* [88] */
     /* fn textureGatherCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> vec4<f32> */
     /* fn textureGatherCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, @const offset: vec2<i32>) -> vec4<f32> */
     /* fn textureGatherCompare<A : iu32>(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: A, depth_ref: f32) -> vec4<f32> */
@@ -14612,7 +14653,7 @@
     /* overloads */ &kOverloads[190],
   },
   {
-    /* [88] */
+    /* [89] */
     /* fn textureNumLayers<T : fiu32>(texture: texture_2d_array<T>) -> u32 */
     /* fn textureNumLayers<T : fiu32>(texture: texture_cube_array<T>) -> u32 */
     /* fn textureNumLayers(texture: texture_depth_2d_array) -> u32 */
@@ -14622,7 +14663,7 @@
     /* overloads */ &kOverloads[242],
   },
   {
-    /* [89] */
+    /* [90] */
     /* fn textureNumLevels<T : fiu32>(texture: texture_1d<T>) -> u32 */
     /* fn textureNumLevels<T : fiu32>(texture: texture_2d<T>) -> u32 */
     /* fn textureNumLevels<T : fiu32>(texture: texture_2d_array<T>) -> u32 */
@@ -14637,14 +14678,14 @@
     /* overloads */ &kOverloads[107],
   },
   {
-    /* [90] */
+    /* [91] */
     /* fn textureNumSamples<T : fiu32>(texture: texture_multisampled_2d<T>) -> u32 */
     /* fn textureNumSamples(texture: texture_depth_multisampled_2d) -> u32 */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[388],
+    /* overloads */ &kOverloads[390],
   },
   {
-    /* [91] */
+    /* [92] */
     /* fn textureSample(texture: texture_1d<f32>, sampler: sampler, coords: f32) -> vec4<f32> */
     /* fn textureSample(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>) -> vec4<f32> */
     /* fn textureSample(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, @const offset: vec2<i32>) -> vec4<f32> */
@@ -14664,7 +14705,7 @@
     /* overloads */ &kOverloads[57],
   },
   {
-    /* [92] */
+    /* [93] */
     /* fn textureSampleBias(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, bias: f32) -> vec4<f32> */
     /* fn textureSampleBias(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, bias: f32, @const offset: vec2<i32>) -> vec4<f32> */
     /* fn textureSampleBias<A : iu32>(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: A, bias: f32) -> vec4<f32> */
@@ -14677,7 +14718,7 @@
     /* overloads */ &kOverloads[144],
   },
   {
-    /* [93] */
+    /* [94] */
     /* fn textureSampleCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32 */
     /* fn textureSampleCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, @const offset: vec2<i32>) -> f32 */
     /* fn textureSampleCompare<A : iu32>(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: A, depth_ref: f32) -> f32 */
@@ -14685,10 +14726,10 @@
     /* fn textureSampleCompare(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32 */
     /* fn textureSampleCompare<A : iu32>(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: A, depth_ref: f32) -> f32 */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[178],
+    /* overloads */ &kOverloads[166],
   },
   {
-    /* [94] */
+    /* [95] */
     /* fn textureSampleCompareLevel(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32 */
     /* fn textureSampleCompareLevel(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, @const offset: vec2<i32>) -> f32 */
     /* fn textureSampleCompareLevel<A : iu32>(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: A, depth_ref: f32) -> f32 */
@@ -14696,10 +14737,10 @@
     /* fn textureSampleCompareLevel(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32 */
     /* fn textureSampleCompareLevel<A : iu32>(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: A, depth_ref: f32) -> f32 */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[166],
+    /* overloads */ &kOverloads[172],
   },
   {
-    /* [95] */
+    /* [96] */
     /* fn textureSampleGrad(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32> */
     /* fn textureSampleGrad(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>, @const offset: vec2<i32>) -> vec4<f32> */
     /* fn textureSampleGrad<A : iu32>(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: A, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32> */
@@ -14712,7 +14753,7 @@
     /* overloads */ &kOverloads[152],
   },
   {
-    /* [96] */
+    /* [97] */
     /* fn textureSampleLevel(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, level: f32) -> vec4<f32> */
     /* fn textureSampleLevel(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, level: f32, @const offset: vec2<i32>) -> vec4<f32> */
     /* fn textureSampleLevel<A : iu32>(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: A, level: f32) -> vec4<f32> */
@@ -14732,14 +14773,14 @@
     /* overloads */ &kOverloads[27],
   },
   {
-    /* [97] */
+    /* [98] */
     /* fn textureSampleBaseClampToEdge(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>) -> vec4<f32> */
     /* fn textureSampleBaseClampToEdge(texture: texture_external, sampler: sampler, coords: vec2<f32>) -> vec4<f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[402],
+    /* overloads */ &kOverloads[404],
   },
   {
-    /* [98] */
+    /* [99] */
     /* fn textureStore<C : iu32>(texture: texture_storage_1d<f32_texel_format, write>, coords: C, value: vec4<f32>) */
     /* fn textureStore<C : iu32>(texture: texture_storage_2d<f32_texel_format, write>, coords: vec2<C>, value: vec4<f32>) */
     /* fn textureStore<C : iu32, A : iu32>(texture: texture_storage_2d_array<f32_texel_format, write>, coords: vec2<C>, array_index: A, value: vec4<f32>) */
@@ -14753,10 +14794,10 @@
     /* fn textureStore<C : iu32, A : iu32>(texture: texture_storage_2d_array<u32_texel_format, write>, coords: vec2<C>, array_index: A, value: vec4<u32>) */
     /* fn textureStore(texture: texture_storage_3d<u32_texel_format, write>, coords: vec3<i32>, value: vec4<u32>) */
     /* num overloads */ 12,
-    /* overloads */ &kOverloads[84],
+    /* overloads */ &kOverloads[72],
   },
   {
-    /* [99] */
+    /* [100] */
     /* fn textureLoad<T : fiu32, C : iu32, L : iu32>(texture: texture_1d<T>, coords: C, level: L) -> vec4<T> */
     /* fn textureLoad<T : fiu32, C : iu32, L : iu32>(texture: texture_2d<T>, coords: vec2<C>, level: L) -> vec4<T> */
     /* fn textureLoad<T : fiu32, C : iu32, A : iu32, L : iu32>(texture: texture_2d_array<T>, coords: vec2<C>, array_index: A, level: L) -> vec4<T> */
@@ -14770,76 +14811,76 @@
     /* overloads */ &kOverloads[135],
   },
   {
-    /* [100] */
+    /* [101] */
     /* fn atomicLoad<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[446],
+    /* overloads */ &kOverloads[449],
   },
   {
-    /* [101] */
+    /* [102] */
     /* fn atomicStore<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) */
     /* num overloads */ 1,
+    /* overloads */ &kOverloads[448],
+  },
+  {
+    /* [103] */
+    /* fn atomicAdd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[445],
+  },
+  {
+    /* [104] */
+    /* fn atomicSub<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[444],
+  },
+  {
+    /* [105] */
+    /* fn atomicMax<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* num overloads */ 1,
     /* overloads */ &kOverloads[443],
   },
   {
-    /* [102] */
-    /* fn atomicAdd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* [106] */
+    /* fn atomicMin<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[442],
   },
   {
-    /* [103] */
-    /* fn atomicSub<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* [107] */
+    /* fn atomicAnd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[441],
   },
   {
-    /* [104] */
-    /* fn atomicMax<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* [108] */
+    /* fn atomicOr<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[440],
   },
   {
-    /* [105] */
-    /* fn atomicMin<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* [109] */
+    /* fn atomicXor<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[439],
   },
   {
-    /* [106] */
-    /* fn atomicAnd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* [110] */
+    /* fn atomicExchange<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[438],
   },
   {
-    /* [107] */
-    /* fn atomicOr<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* [111] */
+    /* fn atomicCompareExchangeWeak<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T, T) -> __atomic_compare_exchange_result<T> */
     /* num overloads */ 1,
     /* overloads */ &kOverloads[437],
   },
   {
-    /* [108] */
-    /* fn atomicXor<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[436],
-  },
-  {
-    /* [109] */
-    /* fn atomicExchange<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[435],
-  },
-  {
-    /* [110] */
-    /* fn atomicCompareExchangeWeak<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T, T) -> __atomic_compare_exchange_result<T> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[452],
-  },
-  {
-    /* [111] */
+    /* [112] */
     /* fn _tint_materialize<T>(T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[470],
+    /* overloads */ &kOverloads[436],
   },
 };
 
@@ -14856,14 +14897,14 @@
     /* op ~<T : ia_iu32>(T) -> T */
     /* op ~<T : ia_iu32, N : num>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[428],
+    /* overloads */ &kOverloads[432],
   },
   {
     /* [2] */
     /* op -<T : fia_fi32_f16>(T) -> T */
     /* op -<T : fia_fi32_f16, N : num>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[426],
+    /* overloads */ &kOverloads[430],
   },
 };
 constexpr uint8_t kUnaryOperatorNot = 0;
@@ -14928,7 +14969,7 @@
     /* op ^<T : ia_iu32>(T, T) -> T */
     /* op ^<T : ia_iu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[414],
+    /* overloads */ &kOverloads[418],
   },
   {
     /* [6] */
@@ -14952,55 +14993,55 @@
     /* [8] */
     /* op &&(bool, bool) -> bool */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[444],
+    /* overloads */ &kOverloads[446],
   },
   {
     /* [9] */
     /* op ||(bool, bool) -> bool */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[445],
+    /* overloads */ &kOverloads[447],
   },
   {
     /* [10] */
     /* op ==<T : scalar>(T, T) -> bool */
     /* op ==<T : scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[408],
+    /* overloads */ &kOverloads[412],
   },
   {
     /* [11] */
     /* op !=<T : scalar>(T, T) -> bool */
     /* op !=<T : scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[394],
+    /* overloads */ &kOverloads[410],
   },
   {
     /* [12] */
     /* op <<T : fia_fiu32_f16>(T, T) -> bool */
     /* op <<T : fia_fiu32_f16, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[376],
+    /* overloads */ &kOverloads[394],
   },
   {
     /* [13] */
     /* op ><T : fia_fiu32_f16>(T, T) -> bool */
     /* op ><T : fia_fiu32_f16, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[374],
+    /* overloads */ &kOverloads[378],
   },
   {
     /* [14] */
     /* op <=<T : fia_fiu32_f16>(T, T) -> bool */
     /* op <=<T : fia_fiu32_f16, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[372],
+    /* overloads */ &kOverloads[376],
   },
   {
     /* [15] */
     /* op >=<T : fia_fiu32_f16>(T, T) -> bool */
     /* op >=<T : fiu32_f16, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[370],
+    /* overloads */ &kOverloads[374],
   },
   {
     /* [16] */
@@ -15016,7 +15057,7 @@
     /* op >><T : iu32>(T, u32) -> T */
     /* op >><T : iu32, N : num>(vec<N, T>, vec<N, u32>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[366],
+    /* overloads */ &kOverloads[370],
   },
 };
 constexpr uint8_t kBinaryOperatorPlus = 0;
@@ -15160,7 +15201,7 @@
     /* conv mat2x4<T : f16>(mat2x4<f32>) -> mat2x4<f16> */
     /* conv mat2x4<T : f32>(mat2x4<f16>) -> mat2x4<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[172],
+    /* overloads */ &kOverloads[178],
   },
   {
     /* [11] */
diff --git a/src/tint/resolver/override_test.cc b/src/tint/resolver/override_test.cc
index 4fdbcee..a12d3c1 100644
--- a/src/tint/resolver/override_test.cc
+++ b/src/tint/resolver/override_test.cc
@@ -91,7 +91,7 @@
 
     EXPECT_FALSE(r()->Resolve());
 
-    EXPECT_EQ(r()->error(), R"(56:78 error: override IDs must be unique
+    EXPECT_EQ(r()->error(), R"(56:78 error: @id values must be unique
 12:34 note: a override with an ID of 7 was previously declared here:)");
 }
 
@@ -100,7 +100,7 @@
 
     EXPECT_FALSE(r()->Resolve());
 
-    EXPECT_EQ(r()->error(), "12:34 error: override IDs must be between 0 and 65535");
+    EXPECT_EQ(r()->error(), "12:34 error: @id value must be between 0 and 65535");
 }
 
 TEST_F(ResolverOverrideTest, F16_TemporallyBan) {
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 3252829..cb24955 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -461,18 +461,18 @@
             return nullptr;
         }
         if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
-            AddError("'id' must be an i32 or u32 value", id_attr->source);
+            AddError("@id must be an i32 or u32 value", id_attr->source);
             return nullptr;
         }
 
         auto const_value = materialized->ConstantValue();
         auto value = const_value->As<AInt>();
         if (value < 0) {
-            AddError("'id' value must be non-negative", id_attr->source);
+            AddError("@id value must be non-negative", id_attr->source);
             return nullptr;
         }
         if (value > std::numeric_limits<decltype(OverrideId::value)>::max()) {
-            AddError("override IDs must be between 0 and " +
+            AddError("@id value must be between 0 and " +
                          std::to_string(std::numeric_limits<decltype(OverrideId::value)>::max()),
                      id_attr->source);
             return nullptr;
@@ -638,14 +638,14 @@
                     return nullptr;
                 }
                 if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
-                    AddError("'binding' must be an i32 or u32 value", attr->source);
+                    AddError("@binding must be an i32 or u32 value", attr->source);
                     return nullptr;
                 }
 
                 auto const_value = materialized->ConstantValue();
                 auto value = const_value->As<AInt>();
                 if (value < 0) {
-                    AddError("'binding' value must be non-negative", attr->source);
+                    AddError("@binding value must be non-negative", attr->source);
                     return nullptr;
                 }
                 binding = u32(value);
@@ -662,14 +662,14 @@
                     return nullptr;
                 }
                 if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
-                    AddError("'group' must be an i32 or u32 value", attr->source);
+                    AddError("@group must be an i32 or u32 value", attr->source);
                     return nullptr;
                 }
 
                 auto const_value = materialized->ConstantValue();
                 auto value = const_value->As<AInt>();
                 if (value < 0) {
-                    AddError("'group' value must be non-negative", attr->source);
+                    AddError("@group value must be non-negative", attr->source);
                     return nullptr;
                 }
                 group = u32(value);
@@ -679,22 +679,11 @@
 
         std::optional<uint32_t> location;
         if (auto* attr = ast::GetAttribute<ast::LocationAttribute>(var->attributes)) {
-            auto* materialized = Materialize(Expression(attr->expr));
-            if (!materialized) {
+            auto value = LocationAttribute(attr);
+            if (!value) {
                 return nullptr;
             }
-            if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
-                AddError("'location' must be an i32 or u32 value", attr->source);
-                return nullptr;
-            }
-
-            auto const_value = materialized->ConstantValue();
-            auto value = const_value->As<AInt>();
-            if (value < 0) {
-                AddError("'location' value must be non-negative", attr->source);
-                return nullptr;
-            }
-            location = u32(value);
+            location = value.Get();
         }
 
         sem = builder_->create<sem::GlobalVariable>(
@@ -748,48 +737,36 @@
     sem::BindingPoint binding_point;
     if (param->HasBindingPoint()) {
         {
+            ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@binding value"};
+            TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
             auto* attr = ast::GetAttribute<ast::BindingAttribute>(param->attributes);
-            auto* materialize = Materialize(Expression(attr->expr));
-            if (!materialize) {
+            auto* materialized = Materialize(Expression(attr->expr));
+            if (!materialized) {
                 return nullptr;
             }
-            auto* c = materialize->ConstantValue();
-            if (!c) {
-                // TODO(crbug.com/tint/1633): Add error message about invalid materialization when
-                // binding can be an expression.
-                return nullptr;
-            }
-            binding_point.binding = c->As<uint32_t>();
+            binding_point.binding = materialized->ConstantValue()->As<uint32_t>();
         }
         {
+            ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@group value"};
+            TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
             auto* attr = ast::GetAttribute<ast::GroupAttribute>(param->attributes);
-            auto* materialize = Materialize(Expression(attr->expr));
-            if (!materialize) {
+            auto* materialized = Materialize(Expression(attr->expr));
+            if (!materialized) {
                 return nullptr;
             }
-            auto* c = materialize->ConstantValue();
-            if (!c) {
-                // TODO(crbug.com/tint/1633): Add error message about invalid materialization when
-                // binding can be an expression.
-                return nullptr;
-            }
-            binding_point.group = c->As<uint32_t>();
+            binding_point.group = materialized->ConstantValue()->As<uint32_t>();
         }
     }
 
     std::optional<uint32_t> location;
-    if (auto* l = ast::GetAttribute<ast::LocationAttribute>(param->attributes)) {
-        auto* materialize = Materialize(Expression(l->expr));
-        if (!materialize) {
+    if (auto* attr = ast::GetAttribute<ast::LocationAttribute>(param->attributes)) {
+        auto value = LocationAttribute(attr);
+        if (!value) {
             return nullptr;
         }
-        auto* c = materialize->ConstantValue();
-        if (!c) {
-            // TODO(crbug.com/tint/1633): Add error message about invalid materialization when
-            // location can be an expression.
-            return nullptr;
-        }
-        location = c->As<uint32_t>();
+        location = value.Get();
     }
 
     auto* sem = builder_->create<sem::Parameter>(
@@ -799,6 +776,30 @@
     return sem;
 }
 
+utils::Result<uint32_t> Resolver::LocationAttribute(const ast::LocationAttribute* attr) {
+    ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@location value"};
+    TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
+    auto* materialized = Materialize(Expression(attr->expr));
+    if (!materialized) {
+        return utils::Failure;
+    }
+
+    if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
+        AddError("@location must be an i32 or u32 value", attr->source);
+        return utils::Failure;
+    }
+
+    auto const_value = materialized->ConstantValue();
+    auto value = const_value->As<AInt>();
+    if (value < 0) {
+        AddError("@location value must be non-negative", attr->source);
+        return utils::Failure;
+    }
+
+    return static_cast<uint32_t>(value);
+}
+
 ast::Access Resolver::DefaultAccessForAddressSpace(ast::AddressSpace address_space) {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
     switch (address_space) {
@@ -984,18 +985,12 @@
     for (auto* attr : decl->return_type_attributes) {
         Mark(attr);
 
-        if (auto* a = attr->As<ast::LocationAttribute>()) {
-            auto* materialize = Materialize(Expression(a->expr));
-            if (!materialize) {
+        if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
+            auto value = LocationAttribute(loc_attr);
+            if (!value) {
                 return nullptr;
             }
-            auto* c = materialize->ConstantValue();
-            if (!c) {
-                // TODO(crbug.com/tint/1633): Add error message about invalid materialization when
-                // location can be an expression.
-                return nullptr;
-            }
-            return_location = c->As<uint32_t>();
+            return_location = value.Get();
         }
     }
     if (!validator_.NoDuplicateAttributes(decl->attributes)) {
@@ -2969,22 +2964,12 @@
                     has_size_attr = true;
                     return true;
                 },
-                [&](const ast::LocationAttribute* l) {
-                    ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant,
-                                                       "@location"};
-                    TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
-
-                    auto* materialize = Materialize(Expression(l->expr));
-                    if (!materialize) {
+                [&](const ast::LocationAttribute* loc_attr) {
+                    auto value = LocationAttribute(loc_attr);
+                    if (!value) {
                         return false;
                     }
-                    auto* c = materialize->ConstantValue();
-                    if (!c) {
-                        // TODO(crbug.com/tint/1633): Add error message about invalid
-                        // materialization when location can be an expression.
-                        return false;
-                    }
-                    location = c->As<uint32_t>();
+                    location = value.Get();
                     return true;
                 },
                 [&](Default) {
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index b657b46..89aab13 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -349,6 +349,10 @@
     /// @param index the index of the parameter
     sem::Parameter* Parameter(const ast::Parameter* param, uint32_t index);
 
+    /// @returns the location value for a `@location` attribute, validating the value's range and
+    /// type.
+    utils::Result<uint32_t> LocationAttribute(const ast::LocationAttribute* attr);
+
     /// Records the address space usage for the given type, and any transient
     /// dependencies of the type. Validates that the type can be used for the
     /// given address space, erroring if it cannot.
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index 831e49e..d5ff803 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -1121,11 +1121,7 @@
                     v1_cf->AddEdge(v1);
 
                     auto [cf2, v2] = ProcessExpression(v1_cf, b->rhs);
-
-                    if (sem_.Get(b)->Behaviors() == sem::Behaviors{sem::Behavior::kNext}) {
-                        return std::pair<Node*, Node*>(cf, v2);
-                    }
-                    return std::pair<Node*, Node*>(cf2, v2);
+                    return std::pair<Node*, Node*>(cf, v2);
                 } else {
                     auto [cf1, v1] = ProcessExpression(cf, b->lhs);
                     auto [cf2, v2] = ProcessExpression(cf1, b->rhs);
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index faefb13..fbaeff9 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -6939,6 +6939,43 @@
 )");
 }
 
+TEST_F(UniformityAnalysisTest, ShortCircuiting_UniformLHS) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read> uniform_global : i32;
+
+fn main() {
+  let b = (uniform_global == 0) && (dpdx(1.0) == 0.0);
+}
+)";
+
+    RunTest(src, true);
+}
+
+TEST_F(UniformityAnalysisTest, ShortCircuiting_NonUniformLHS) {
+    std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
+
+fn main() {
+  let b = (non_uniform_global == 0) && (dpdx(1.0) == 0.0);
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:5:41 warning: 'dpdx' must only be called from uniform control flow
+  let b = (non_uniform_global == 0) && (dpdx(1.0) == 0.0);
+                                        ^^^^
+
+test:5:37 note: control flow depends on non-uniform value
+  let b = (non_uniform_global == 0) && (dpdx(1.0) == 0.0);
+                                    ^^
+
+test:5:12 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
+  let b = (non_uniform_global == 0) && (dpdx(1.0) == 0.0);
+           ^^^^^^^^^^^^^^^^^^
+)");
+}
+
 TEST_F(UniformityAnalysisTest, ShortCircuiting_ReconvergeLHS) {
     std::string src = R"(
 @group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 7f1cecf..f96af05 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -797,7 +797,7 @@
         if (attr->Is<ast::IdAttribute>()) {
             auto id = v->OverrideId();
             if (auto it = override_ids.find(id); it != override_ids.end() && it->second != v) {
-                AddError("override IDs must be unique", attr->source);
+                AddError("@id values must be unique", attr->source);
                 AddNote("a override with an ID of " + std::to_string(id.value) +
                             " was previously declared here:",
                         ast::GetAttribute<ast::IdAttribute>(it->second->Declaration()->attributes)
@@ -1382,6 +1382,14 @@
         AddError(std::string(constraint) + " requires " + stage_name(latest_stage) +
                      ", but expression is " + stage_name(expr->Stage()),
                  expr->Declaration()->source);
+
+        if (auto* stmt = expr->Stmt()) {
+            if (auto* decl = As<ast::VariableDeclStatement>(stmt->Declaration())) {
+                if (decl->variable->Is<ast::Const>()) {
+                    AddNote("consider changing 'const' to 'let'", decl->source);
+                }
+            }
+        }
         return false;
     }
     return true;
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index d302264..964c8d9 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -417,10 +417,8 @@
     EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
 }
 
-TEST_F(ResolverVariableValidationTest, ConstInitWithVar) {
-    auto* v = Var("v", Expr(1_i));
-    auto* c = Const("c", Expr(Source{{12, 34}}, v));
-    WrapInFunction(v, c);
+TEST_F(ResolverVariableValidationTest, GlobalConstWithRuntimeExpression) {
+    GlobalConst("c", Call(Source{{12, 34}}, "dpdx", 1._a));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -428,47 +426,64 @@
         R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
 }
 
+TEST_F(ResolverVariableValidationTest, ConstInitWithVar) {
+    auto* v = Var("v", Expr(1_i));
+    auto* c = Const("c", Expr(Source{{12, 34}}, v));
+    WrapInFunction(v, Decl(Source{{56, 78}}, c));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression
+56:78 note: consider changing 'const' to 'let')");
+}
+
 TEST_F(ResolverVariableValidationTest, ConstInitWithOverride) {
     auto* o = Override("v", Expr(1_i));
     auto* c = Const("c", Expr(Source{{12, 34}}, o));
-    WrapInFunction(c);
+    WrapInFunction(Decl(Source{{56, 78}}, c));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(12:34 error: const initializer requires a const-expression, but expression is an override-expression)");
+        R"(12:34 error: const initializer requires a const-expression, but expression is an override-expression
+56:78 note: consider changing 'const' to 'let')");
 }
 
 TEST_F(ResolverVariableValidationTest, ConstInitWithLet) {
     auto* l = Let("v", Expr(1_i));
     auto* c = Const("c", Expr(Source{{12, 34}}, l));
-    WrapInFunction(l, c);
+    WrapInFunction(l, Decl(Source{{56, 78}}, c));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
+        R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression
+56:78 note: consider changing 'const' to 'let')");
 }
 
 TEST_F(ResolverVariableValidationTest, ConstInitWithRuntimeExpr) {
     // const c = clamp(2, dpdx(0.5), 3);
-    WrapInFunction(Const("c", Call("clamp", 2_a, Call(Source{{12, 34}}, "dpdx", 0.5_a), 3_a)));
+    auto* c = Const("c", Call("clamp", 2_a, Call(Source{{12, 34}}, "dpdx", 0.5_a), 3_a));
+    WrapInFunction(Decl(Source{{56, 78}}, c));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
+        R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression
+56:78 note: consider changing 'const' to 'let')");
 }
 
 TEST_F(ResolverVariableValidationTest, ConstInitWithOverrideExpr) {
     auto* o = Override("v", Expr(1_i));
     auto* c = Const("c", Add(10_a, Expr(Source{{12, 34}}, o)));
-    WrapInFunction(c);
+    WrapInFunction(Decl(Source{{56, 78}}, c));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(12:34 error: const initializer requires a const-expression, but expression is an override-expression)");
+        R"(12:34 error: const initializer requires a const-expression, but expression is an override-expression
+56:78 note: consider changing 'const' to 'let')");
 }
 
 }  // namespace
diff --git a/src/tint/sem/builtin_type.cc b/src/tint/sem/builtin_type.cc
index d010d89..899fbd3 100644
--- a/src/tint/sem/builtin_type.cc
+++ b/src/tint/sem/builtin_type.cc
@@ -210,6 +210,9 @@
     if (name == "pow") {
         return BuiltinType::kPow;
     }
+    if (name == "quantizeToF16") {
+        return BuiltinType::kQuantizeToF16;
+    }
     if (name == "radians") {
         return BuiltinType::kRadians;
     }
@@ -492,6 +495,8 @@
             return "pack4x8unorm";
         case BuiltinType::kPow:
             return "pow";
+        case BuiltinType::kQuantizeToF16:
+            return "quantizeToF16";
         case BuiltinType::kRadians:
             return "radians";
         case BuiltinType::kReflect:
diff --git a/src/tint/sem/builtin_type.h b/src/tint/sem/builtin_type.h
index 05c2def..0cfc48e 100644
--- a/src/tint/sem/builtin_type.h
+++ b/src/tint/sem/builtin_type.h
@@ -92,6 +92,7 @@
     kPack4X8Snorm,
     kPack4X8Unorm,
     kPow,
+    kQuantizeToF16,
     kRadians,
     kReflect,
     kRefract,
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index 8e271d7..634f5da 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -23,6 +23,7 @@
 
 #include "src/tint/ast/access.h"
 #include "src/tint/ast/address_space.h"
+#include "src/tint/ast/parameter.h"
 #include "src/tint/sem/binding_point.h"
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/parameter_usage.h"
@@ -212,6 +213,11 @@
     /// Destructor
     ~Parameter() override;
 
+    /// @returns the AST declaration node
+    const ast::Parameter* Declaration() const {
+        return static_cast<const ast::Parameter*>(Variable::Declaration());
+    }
+
     /// @return the index of the parmeter in the function
     uint32_t Index() const { return index_; }
 
diff --git a/src/tint/test_main.cc b/src/tint/test_main.cc
index ca68271..918bd20 100644
--- a/src/tint/test_main.cc
+++ b/src/tint/test_main.cc
@@ -14,15 +14,12 @@
 
 #include "gmock/gmock.h"
 #include "src/tint/program.h"
+#include "tint/tint.h"
 
 #if TINT_BUILD_SPV_READER
 #include "src/tint/reader/spirv/parser_impl_test_helper.h"
 #endif
 
-#if TINT_BUILD_WGSL_WRITER
-#include "src/tint/writer/wgsl/generator.h"
-#endif
-
 namespace {
 
 void TintInternalCompilerErrorReporter(const tint::diag::List& diagnostics) {
@@ -54,15 +51,7 @@
 int main(int argc, char** argv) {
     testing::InitGoogleMock(&argc, argv);
 
-#if TINT_BUILD_WGSL_WRITER
-    tint::Program::printer = [](const tint::Program* program) {
-        auto result = tint::writer::wgsl::Generate(program, {});
-        if (!result.error.empty()) {
-            return "error: " + result.error;
-        }
-        return result.wgsl;
-    };
-#endif  // TINT_BUILD_WGSL_WRITER
+    tint::Initialize();
 
     Flags flags;
     if (!flags.parse(argc, argv)) {
@@ -79,5 +68,7 @@
 
     auto res = RUN_ALL_TESTS();
 
+    tint::Shutdown();
+
     return res;
 }
diff --git a/src/tint/tint.cc b/src/tint/tint.cc
new file mode 100644
index 0000000..cea69cb
--- /dev/null
+++ b/src/tint/tint.cc
@@ -0,0 +1,36 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tint/tint.h"
+
+namespace tint {
+
+void Initialize() {
+#if TINT_BUILD_WGSL_WRITER
+    // Register the Program printer. This is used for debugging purposes.
+    tint::Program::printer = [](const tint::Program* program) {
+        auto result = writer::wgsl::Generate(program, {});
+        if (!result.error.empty()) {
+            return "error: " + result.error;
+        }
+        return result.wgsl;
+    };
+#endif
+}
+
+void Shutdown() {
+    // Currently no-op, but may release tint resources in the future.
+}
+
+}  // namespace tint
diff --git a/src/tint/transform/add_block_attribute.cc b/src/tint/transform/add_block_attribute.cc
index 97c531e..513925f 100644
--- a/src/tint/transform/add_block_attribute.cc
+++ b/src/tint/transform/add_block_attribute.cc
@@ -27,97 +27,78 @@
 
 namespace tint::transform {
 
-namespace {
-
-bool IsUsedAsNonBuffer(const std::unordered_set<tint::ast::AddressSpace>& uses) {
-    for (auto use : uses) {
-        if (!ast::IsHostShareable(use)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
 AddBlockAttribute::AddBlockAttribute() = default;
 
 AddBlockAttribute::~AddBlockAttribute() = default;
 
-void AddBlockAttribute::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    auto& sem = ctx.src->Sem();
+Transform::ApplyResult AddBlockAttribute::Apply(const Program* src,
+                                                const DataMap&,
+                                                DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
 
-    // Collect the set of structs that are nested in other types.
-    utils::Hashset<const sem::Struct*, 8> nested_structs;
-    for (auto* ty : ctx.src->Types()) {
-        Switch(
-            ty,
-            [&](const sem::Array* arr) {
-                if (auto* nested_str = arr->ElemType()->As<sem::Struct>()) {
-                    nested_structs.Add(nested_str);
-                }
-            },
-            [&](const sem::Struct* str) {
-                for (auto* member : str->Members()) {
-                    if (auto* nested_str = member->Type()->As<sem::Struct>()) {
-                        nested_structs.Add(nested_str);
-                    }
-                }
-            });
-    }
+    auto& sem = src->Sem();
 
     // A map from a type in the source program to a block-decorated wrapper that contains it in the
     // destination program.
     utils::Hashmap<const sem::Type*, const ast::Struct*, 8> wrapper_structs;
 
     // Process global 'var' declarations that are buffers.
-    for (auto* global : ctx.src->AST().GlobalVariables()) {
+    bool made_changes = false;
+    for (auto* global : src->AST().GlobalVariables()) {
         auto* var = sem.Get(global);
         if (!ast::IsHostShareable(var->AddressSpace())) {
             // Not declared in a host-sharable address space
             continue;
         }
 
+        made_changes = true;
+
         auto* ty = var->Type()->UnwrapRef();
         auto* str = ty->As<sem::Struct>();
-        bool needs_wrapping =
-            !str ||                                       // Type is not a structure
-            nested_structs.Contains(str) ||               // Structure is nested by another type
-            IsUsedAsNonBuffer(str->AddressSpaceUsage());  // Structure is used as a non-buffer usage
+
+        // Always try to wrap the buffer type into a struct. We can not do so only if it is a struct
+        // but without a fixed footprint, i.e. contains a runtime-sized array as its member. Note
+        // that such struct type can be only used as storage buffer variables' type. Also note that
+        // any buffer struct type that may be nested by another type must have a fixed footprint,
+        // therefore will be wrapped.
+        bool needs_wrapping = !str ||                    // Type is not a structure
+                              str->HasFixedFootprint();  // Struct has a fixed footprint
 
         if (needs_wrapping) {
             const char* kMemberName = "inner";
 
-            // This is a non-struct or a struct that is nested somewhere else, so we
-            // need to wrap it first.
             auto* wrapper = wrapper_structs.GetOrCreate(ty, [&] {
-                auto* block = ctx.dst->ASTNodes().Create<BlockAttribute>(ctx.dst->ID(),
-                                                                         ctx.dst->AllocateNodeID());
-                auto wrapper_name = ctx.src->Symbols().NameFor(global->symbol) + "_block";
-                auto* ret = ctx.dst->create<ast::Struct>(
-                    ctx.dst->Symbols().New(wrapper_name),
-                    utils::Vector{ctx.dst->Member(kMemberName, CreateASTTypeFor(ctx, ty))},
+                auto* block = b.ASTNodes().Create<BlockAttribute>(b.ID(), b.AllocateNodeID());
+                auto wrapper_name = src->Symbols().NameFor(global->symbol) + "_block";
+                auto* ret = b.create<ast::Struct>(
+                    b.Symbols().New(wrapper_name),
+                    utils::Vector{b.Member(kMemberName, CreateASTTypeFor(ctx, ty))},
                     utils::Vector{block});
-                ctx.InsertBefore(ctx.src->AST().GlobalDeclarations(), global, ret);
+                ctx.InsertBefore(src->AST().GlobalDeclarations(), global, ret);
                 return ret;
             });
-            ctx.Replace(global->type, ctx.dst->ty.Of(wrapper));
+            ctx.Replace(global->type, b.ty.Of(wrapper));
 
             // Insert a member accessor to get the original type from the wrapper at
             // any usage of the original variable.
             for (auto* user : var->Users()) {
                 ctx.Replace(user->Declaration(),
-                            ctx.dst->MemberAccessor(ctx.Clone(global->symbol), kMemberName));
+                            b.MemberAccessor(ctx.Clone(global->symbol), kMemberName));
             }
         } else {
             // Add a block attribute to this struct directly.
-            auto* block = ctx.dst->ASTNodes().Create<BlockAttribute>(ctx.dst->ID(),
-                                                                     ctx.dst->AllocateNodeID());
+            auto* block = b.ASTNodes().Create<BlockAttribute>(b.ID(), b.AllocateNodeID());
             ctx.InsertFront(str->Declaration()->attributes, block);
         }
     }
 
+    if (!made_changes) {
+        return SkipTransform;
+    }
+
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 AddBlockAttribute::BlockAttribute::BlockAttribute(ProgramID pid, ast::NodeID nid)
diff --git a/src/tint/transform/add_block_attribute.h b/src/tint/transform/add_block_attribute.h
index 69dfab5..d5e8c4e 100644
--- a/src/tint/transform/add_block_attribute.h
+++ b/src/tint/transform/add_block_attribute.h
@@ -22,11 +22,8 @@
 
 namespace tint::transform {
 
-/// AddBlockAttribute is a transform that adds an
-/// `@internal(block)` attribute to any structure that is used as the
-/// store type of a buffer. If that structure is nested inside another structure
-/// or an array, then it is wrapped inside another structure which gets the
-/// `@internal(block)` attribute instead.
+/// AddBlockAttribute is a transform that wrap the store type of a buffer into a struct if possible,
+/// then adds an `@internal(block)` attribute to the wrapper struct.
 class AddBlockAttribute final : public Castable<AddBlockAttribute, Transform> {
   public:
     /// BlockAttribute is an InternalAttribute that is used to decorate a
@@ -56,14 +53,10 @@
     /// Destructor
     ~AddBlockAttribute() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/add_block_attribute_test.cc b/src/tint/transform/add_block_attribute_test.cc
index 7bb5efc..e4519dd 100644
--- a/src/tint/transform/add_block_attribute_test.cc
+++ b/src/tint/transform/add_block_attribute_test.cc
@@ -200,6 +200,85 @@
     EXPECT_EQ(expect, str(got));
 }
 
+TEST_F(AddBlockAttributeTest, BasicStruct_Storage_AccessRoot) {
+    auto* src = R"(
+struct S {
+  f : f32,
+};
+
+@group(0) @binding(0)
+var<storage, read_write> s : S;
+
+@fragment
+fn main() {
+  let f = s;
+}
+)";
+    auto* expect = R"(
+struct S {
+  f : f32,
+}
+
+@internal(block)
+struct s_block {
+  inner : S,
+}
+
+@group(0) @binding(0) var<storage, read_write> s : s_block;
+
+@fragment
+fn main() {
+  let f = s.inner;
+}
+)";
+
+    auto got = Run<AddBlockAttribute>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(AddBlockAttributeTest, BasicStruct_Storage_TwoUsage_AccessRoot) {
+    auto* src = R"(
+struct S {
+  f : f32,
+};
+
+@group(0) @binding(0)
+var<storage, read_write> in : S;
+
+@group(0) @binding(1)
+var<storage, read_write> out : S;
+
+@compute @workgroup_size(1)
+fn main() {
+  out = in;
+}
+)";
+    auto* expect = R"(
+struct S {
+  f : f32,
+}
+
+@internal(block)
+struct in_block {
+  inner : S,
+}
+
+@group(0) @binding(0) var<storage, read_write> in : in_block;
+
+@group(0) @binding(1) var<storage, read_write> out : in_block;
+
+@compute @workgroup_size(1)
+fn main() {
+  out.inner = in.inner;
+}
+)";
+
+    auto got = Run<AddBlockAttribute>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
 TEST_F(AddBlockAttributeTest, BasicStruct_AccessField) {
     auto* src = R"(
 struct S {
@@ -215,16 +294,20 @@
 }
 )";
     auto* expect = R"(
-@internal(block)
 struct S {
   f : f32,
 }
 
-@group(0) @binding(0) var<uniform> u : S;
+@internal(block)
+struct u_block {
+  inner : S,
+}
+
+@group(0) @binding(0) var<uniform> u : u_block;
 
 @fragment
 fn main() {
-  let f = u.f;
+  let f = u.inner.f;
 }
 )";
 
@@ -280,16 +363,20 @@
     auto* expect = R"(
 enable chromium_experimental_push_constant;
 
-@internal(block)
 struct S {
   f : f32,
 }
 
-var<push_constant> u : S;
+@internal(block)
+struct u_block {
+  inner : S,
+}
+
+var<push_constant> u : u_block;
 
 @fragment
 fn main() {
-  let f = u.f;
+  let f = u.inner.f;
 }
 )";
 
@@ -321,16 +408,20 @@
   f : f32,
 }
 
-@internal(block)
 struct Outer {
   i : Inner,
 }
 
-@group(0) @binding(0) var<uniform> u : Outer;
+@internal(block)
+struct u_block {
+  inner : Outer,
+}
+
+@group(0) @binding(0) var<uniform> u : u_block;
 
 @fragment
 fn main() {
-  let f = u.i.f;
+  let f = u.inner.i.f;
 }
 )";
 
@@ -366,12 +457,16 @@
   f : f32,
 }
 
-@internal(block)
 struct Outer {
   i : Inner,
 }
 
-@group(0) @binding(0) var<uniform> u0 : Outer;
+@internal(block)
+struct u0_block {
+  inner : Outer,
+}
+
+@group(0) @binding(0) var<uniform> u0 : u0_block;
 
 @internal(block)
 struct u1_block {
@@ -382,7 +477,7 @@
 
 @fragment
 fn main() {
-  let f0 = u0.i.f;
+  let f0 = u0.inner.i.f;
   let f1 = u1.inner.f;
 }
 )";
@@ -474,12 +569,16 @@
   f : f32,
 }
 
-@internal(block)
 struct S {
   i : Inner,
 }
 
-@group(0) @binding(0) var<uniform> u0 : S;
+@internal(block)
+struct u0_block {
+  inner : S,
+}
+
+@group(0) @binding(0) var<uniform> u0 : u0_block;
 
 @internal(block)
 struct u1_block {
@@ -492,7 +591,7 @@
 
 @fragment
 fn main() {
-  let f0 = u0.i.f;
+  let f0 = u0.inner.i.f;
   let f1 = u1.inner.f;
   let f2 = u2.inner.f;
 }
@@ -621,14 +720,18 @@
 
 type MyInner = Inner;
 
-@internal(block)
 struct Outer {
   i : MyInner,
 }
 
 type MyOuter = Outer;
 
-@group(0) @binding(0) var<uniform> u0 : MyOuter;
+@internal(block)
+struct u0_block {
+  inner : Outer,
+}
+
+@group(0) @binding(0) var<uniform> u0 : u0_block;
 
 @internal(block)
 struct u1_block {
@@ -639,7 +742,7 @@
 
 @fragment
 fn main() {
-  let f0 = u0.i.f;
+  let f0 = u0.inner.i.f;
   let f1 = u1.inner.f;
 }
 )";
@@ -678,7 +781,7 @@
     auto* expect = R"(
 @fragment
 fn main() {
-  let f0 = u0.i.f;
+  let f0 = u0.inner.i.f;
   let f1 = u1.inner.f;
 }
 
@@ -691,11 +794,15 @@
 
 type MyInner = Inner;
 
-@group(0) @binding(0) var<uniform> u0 : MyOuter;
+@internal(block)
+struct u0_block {
+  inner : Outer,
+}
+
+@group(0) @binding(0) var<uniform> u0 : u0_block;
 
 type MyOuter = Outer;
 
-@internal(block)
 struct Outer {
   i : MyInner,
 }
@@ -810,18 +917,22 @@
 }
 )";
     auto* expect = R"(
-@internal(block) @internal(block)
 struct S {
   f : f32,
 }
 
-@group(0) @binding(0) var<uniform> u : S;
+@internal(block)
+struct u_block {
+  inner : S,
+}
 
-@group(0) @binding(1) var<storage, read_write> s : S;
+@group(0) @binding(0) var<uniform> u : u_block;
+
+@group(0) @binding(1) var<storage, read_write> s : u_block;
 
 @fragment
 fn main() {
-  s = u;
+  s.inner = u.inner;
 }
 )";
 
@@ -850,5 +961,65 @@
     EXPECT_EQ(expect, str(got));
 }
 
+TEST_F(AddBlockAttributeTest, StorageBufferWithRuntimeArray) {
+    auto* src = R"(
+struct S {
+  f : f32,
+}
+
+struct SWithArr {
+  f : f32,
+  arr : array<f32>,
+}
+
+@group(0) @binding(0)
+var<storage, read> in_1 : S;
+
+@group(0) @binding(1)
+var<storage, read> in_2 : SWithArr;
+
+@group(1) @binding(0)
+var<storage, read_write> out : SWithArr;
+
+@fragment
+fn main() {
+  out.f = in_1.f;
+  out.arr[0] = in_2.arr[1];
+}
+)";
+    auto* expect = R"(
+struct S {
+  f : f32,
+}
+
+@internal(block) @internal(block)
+struct SWithArr {
+  f : f32,
+  arr : array<f32>,
+}
+
+@internal(block)
+struct in_1_block {
+  inner : S,
+}
+
+@group(0) @binding(0) var<storage, read> in_1 : in_1_block;
+
+@group(0) @binding(1) var<storage, read> in_2 : SWithArr;
+
+@group(1) @binding(0) var<storage, read_write> out : SWithArr;
+
+@fragment
+fn main() {
+  out.f = in_1.inner.f;
+  out.arr[0] = in_2.arr[1];
+}
+)";
+
+    auto got = Run<AddBlockAttribute>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
 }  // namespace
 }  // namespace tint::transform
diff --git a/src/tint/transform/add_empty_entry_point.cc b/src/tint/transform/add_empty_entry_point.cc
index 5ef4fe8..f71394d 100644
--- a/src/tint/transform/add_empty_entry_point.cc
+++ b/src/tint/transform/add_empty_entry_point.cc
@@ -23,12 +23,9 @@
 using namespace tint::number_suffixes;  // NOLINT
 
 namespace tint::transform {
+namespace {
 
-AddEmptyEntryPoint::AddEmptyEntryPoint() = default;
-
-AddEmptyEntryPoint::~AddEmptyEntryPoint() = default;
-
-bool AddEmptyEntryPoint::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     for (auto* func : program->AST().Functions()) {
         if (func->IsEntryPoint()) {
             return false;
@@ -37,13 +34,30 @@
     return true;
 }
 
-void AddEmptyEntryPoint::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    ctx.dst->Func(ctx.dst->Symbols().New("unused_entry_point"), {}, ctx.dst->ty.void_(), {},
-                  utils::Vector{
-                      ctx.dst->Stage(ast::PipelineStage::kCompute),
-                      ctx.dst->WorkgroupSize(1_i),
-                  });
+}  // namespace
+
+AddEmptyEntryPoint::AddEmptyEntryPoint() = default;
+
+AddEmptyEntryPoint::~AddEmptyEntryPoint() = default;
+
+Transform::ApplyResult AddEmptyEntryPoint::Apply(const Program* src,
+                                                 const DataMap&,
+                                                 DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
+    b.Func(b.Symbols().New("unused_entry_point"), {}, b.ty.void_(), {},
+           utils::Vector{
+               b.Stage(ast::PipelineStage::kCompute),
+               b.WorkgroupSize(1_i),
+           });
+
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/add_empty_entry_point.h b/src/tint/transform/add_empty_entry_point.h
index 5530355..828f3b5 100644
--- a/src/tint/transform/add_empty_entry_point.h
+++ b/src/tint/transform/add_empty_entry_point.h
@@ -27,19 +27,10 @@
     /// Destructor
     ~AddEmptyEntryPoint() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/array_length_from_uniform.cc b/src/tint/transform/array_length_from_uniform.cc
index 3938b0c..70097f2 100644
--- a/src/tint/transform/array_length_from_uniform.cc
+++ b/src/tint/transform/array_length_from_uniform.cc
@@ -31,13 +31,153 @@
 
 namespace tint::transform {
 
+namespace {
+
+bool ShouldRun(const Program* program) {
+    for (auto* fn : program->AST().Functions()) {
+        if (auto* sem_fn = program->Sem().Get(fn)) {
+            for (auto* builtin : sem_fn->DirectlyCalledBuiltins()) {
+                if (builtin->Type() == sem::BuiltinType::kArrayLength) {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+}  // namespace
+
 ArrayLengthFromUniform::ArrayLengthFromUniform() = default;
 ArrayLengthFromUniform::~ArrayLengthFromUniform() = default;
 
-/// The PIMPL state for this transform
+/// PIMPL state for the transform
 struct ArrayLengthFromUniform::State {
+    /// Constructor
+    /// @param program the source program
+    /// @param in the input transform data
+    /// @param out the output transform data
+    explicit State(const Program* program, const DataMap& in, DataMap& out)
+        : src(program), inputs(in), outputs(out) {}
+
+    /// Runs the transform
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
+        auto* cfg = inputs.Get<Config>();
+        if (cfg == nullptr) {
+            b.Diagnostics().add_error(diag::System::Transform,
+                                      "missing transform data for " +
+                                          std::string(TypeInfo::Of<ArrayLengthFromUniform>().name));
+            return Program(std::move(b));
+        }
+
+        if (!ShouldRun(ctx.src)) {
+            return SkipTransform;
+        }
+
+        const char* kBufferSizeMemberName = "buffer_size";
+
+        // Determine the size of the buffer size array.
+        uint32_t max_buffer_size_index = 0;
+
+        IterateArrayLengthOnStorageVar([&](const ast::CallExpression*, const sem::VariableUser*,
+                                           const sem::GlobalVariable* var) {
+            auto binding = var->BindingPoint();
+            auto idx_itr = cfg->bindpoint_to_size_index.find(binding);
+            if (idx_itr == cfg->bindpoint_to_size_index.end()) {
+                return;
+            }
+            if (idx_itr->second > max_buffer_size_index) {
+                max_buffer_size_index = idx_itr->second;
+            }
+        });
+
+        // Get (or create, on first call) the uniform buffer that will receive the
+        // size of each storage buffer in the module.
+        const ast::Variable* buffer_size_ubo = nullptr;
+        auto get_ubo = [&]() {
+            if (!buffer_size_ubo) {
+                // Emit an array<vec4<u32>, N>, where N is 1/4 number of elements.
+                // We do this because UBOs require an element stride that is 16-byte
+                // aligned.
+                auto* buffer_size_struct = b.Structure(
+                    b.Sym(), utils::Vector{
+                                 b.Member(kBufferSizeMemberName,
+                                          b.ty.array(b.ty.vec4(b.ty.u32()),
+                                                     u32((max_buffer_size_index / 4) + 1))),
+                             });
+                buffer_size_ubo =
+                    b.GlobalVar(b.Sym(), b.ty.Of(buffer_size_struct), ast::AddressSpace::kUniform,
+                                b.Group(AInt(cfg->ubo_binding.group)),
+                                b.Binding(AInt(cfg->ubo_binding.binding)));
+            }
+            return buffer_size_ubo;
+        };
+
+        std::unordered_set<uint32_t> used_size_indices;
+
+        IterateArrayLengthOnStorageVar([&](const ast::CallExpression* call_expr,
+                                           const sem::VariableUser* storage_buffer_sem,
+                                           const sem::GlobalVariable* var) {
+            auto binding = var->BindingPoint();
+            auto idx_itr = cfg->bindpoint_to_size_index.find(binding);
+            if (idx_itr == cfg->bindpoint_to_size_index.end()) {
+                return;
+            }
+
+            uint32_t size_index = idx_itr->second;
+            used_size_indices.insert(size_index);
+
+            // Load the total storage buffer size from the UBO.
+            uint32_t array_index = size_index / 4;
+            auto* vec_expr = b.IndexAccessor(
+                b.MemberAccessor(get_ubo()->symbol, kBufferSizeMemberName), u32(array_index));
+            uint32_t vec_index = size_index % 4;
+            auto* total_storage_buffer_size = b.IndexAccessor(vec_expr, u32(vec_index));
+
+            // Calculate actual array length
+            //                total_storage_buffer_size - array_offset
+            // array_length = ----------------------------------------
+            //                             array_stride
+            const ast::Expression* total_size = total_storage_buffer_size;
+            auto* storage_buffer_type = storage_buffer_sem->Type()->UnwrapRef();
+            const sem::Array* array_type = nullptr;
+            if (auto* str = storage_buffer_type->As<sem::Struct>()) {
+                // The variable is a struct, so subtract the byte offset of the array
+                // member.
+                auto* array_member_sem = str->Members().back();
+                array_type = array_member_sem->Type()->As<sem::Array>();
+                total_size = b.Sub(total_storage_buffer_size, u32(array_member_sem->Offset()));
+            } else if (auto* arr = storage_buffer_type->As<sem::Array>()) {
+                array_type = arr;
+            } else {
+                TINT_ICE(Transform, b.Diagnostics())
+                    << "expected form of arrayLength argument to be &array_var or "
+                       "&struct_var.array_member";
+                return;
+            }
+            auto* array_length = b.Div(total_size, u32(array_type->Stride()));
+
+            ctx.Replace(call_expr, array_length);
+        });
+
+        outputs.Add<Result>(used_size_indices);
+
+        ctx.Clone();
+        return Program(std::move(b));
+    }
+
+  private:
+    /// The source program
+    const Program* const src;
+    /// The transform inputs
+    const DataMap& inputs;
+    /// The transform outputs
+    DataMap& outputs;
+    /// The target program builder
+    ProgramBuilder b;
     /// The clone context
-    CloneContext& ctx;
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
 
     /// Iterate over all arrayLength() builtins that operate on
     /// storage buffer variables.
@@ -48,10 +188,10 @@
     /// sem::GlobalVariable for the storage buffer.
     template <typename F>
     void IterateArrayLengthOnStorageVar(F&& functor) {
-        auto& sem = ctx.src->Sem();
+        auto& sem = src->Sem();
 
         // Find all calls to the arrayLength() builtin.
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
+        for (auto* node : src->ASTNodes().Objects()) {
             auto* call_expr = node->As<ast::CallExpression>();
             if (!call_expr) {
                 continue;
@@ -79,7 +219,7 @@
             //   arrayLength(&array_var)
             auto* param = call_expr->args[0]->As<ast::UnaryOpExpression>();
             if (!param || param->op != ast::UnaryOp::kAddressOf) {
-                TINT_ICE(Transform, ctx.dst->Diagnostics())
+                TINT_ICE(Transform, b.Diagnostics())
                     << "expected form of arrayLength argument to be &array_var or "
                        "&struct_var.array_member";
                 break;
@@ -90,7 +230,7 @@
             }
             auto* storage_buffer_sem = sem.Get<sem::VariableUser>(storage_buffer_expr);
             if (!storage_buffer_sem) {
-                TINT_ICE(Transform, ctx.dst->Diagnostics())
+                TINT_ICE(Transform, b.Diagnostics())
                     << "expected form of arrayLength argument to be &array_var or "
                        "&struct_var.array_member";
                 break;
@@ -99,8 +239,7 @@
             // Get the index to use for the buffer size array.
             auto* var = tint::As<sem::GlobalVariable>(storage_buffer_sem->Variable());
             if (!var) {
-                TINT_ICE(Transform, ctx.dst->Diagnostics())
-                    << "storage buffer is not a global variable";
+                TINT_ICE(Transform, b.Diagnostics()) << "storage buffer is not a global variable";
                 break;
             }
             functor(call_expr, storage_buffer_sem, var);
@@ -108,117 +247,10 @@
     }
 };
 
-bool ArrayLengthFromUniform::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* fn : program->AST().Functions()) {
-        if (auto* sem_fn = program->Sem().Get(fn)) {
-            for (auto* builtin : sem_fn->DirectlyCalledBuiltins()) {
-                if (builtin->Type() == sem::BuiltinType::kArrayLength) {
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
-void ArrayLengthFromUniform::Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const {
-    auto* cfg = inputs.Get<Config>();
-    if (cfg == nullptr) {
-        ctx.dst->Diagnostics().add_error(
-            diag::System::Transform, "missing transform data for " + std::string(TypeInfo().name));
-        return;
-    }
-
-    const char* kBufferSizeMemberName = "buffer_size";
-
-    // Determine the size of the buffer size array.
-    uint32_t max_buffer_size_index = 0;
-
-    State{ctx}.IterateArrayLengthOnStorageVar(
-        [&](const ast::CallExpression*, const sem::VariableUser*, const sem::GlobalVariable* var) {
-            auto binding = var->BindingPoint();
-            auto idx_itr = cfg->bindpoint_to_size_index.find(binding);
-            if (idx_itr == cfg->bindpoint_to_size_index.end()) {
-                return;
-            }
-            if (idx_itr->second > max_buffer_size_index) {
-                max_buffer_size_index = idx_itr->second;
-            }
-        });
-
-    // Get (or create, on first call) the uniform buffer that will receive the
-    // size of each storage buffer in the module.
-    const ast::Variable* buffer_size_ubo = nullptr;
-    auto get_ubo = [&]() {
-        if (!buffer_size_ubo) {
-            // Emit an array<vec4<u32>, N>, where N is 1/4 number of elements.
-            // We do this because UBOs require an element stride that is 16-byte
-            // aligned.
-            auto* buffer_size_struct = ctx.dst->Structure(
-                ctx.dst->Sym(),
-                utils::Vector{
-                    ctx.dst->Member(kBufferSizeMemberName,
-                                    ctx.dst->ty.array(ctx.dst->ty.vec4(ctx.dst->ty.u32()),
-                                                      u32((max_buffer_size_index / 4) + 1))),
-                });
-            buffer_size_ubo = ctx.dst->GlobalVar(ctx.dst->Sym(), ctx.dst->ty.Of(buffer_size_struct),
-                                                 ast::AddressSpace::kUniform,
-                                                 ctx.dst->Group(AInt(cfg->ubo_binding.group)),
-                                                 ctx.dst->Binding(AInt(cfg->ubo_binding.binding)));
-        }
-        return buffer_size_ubo;
-    };
-
-    std::unordered_set<uint32_t> used_size_indices;
-
-    State{ctx}.IterateArrayLengthOnStorageVar([&](const ast::CallExpression* call_expr,
-                                                  const sem::VariableUser* storage_buffer_sem,
-                                                  const sem::GlobalVariable* var) {
-        auto binding = var->BindingPoint();
-        auto idx_itr = cfg->bindpoint_to_size_index.find(binding);
-        if (idx_itr == cfg->bindpoint_to_size_index.end()) {
-            return;
-        }
-
-        uint32_t size_index = idx_itr->second;
-        used_size_indices.insert(size_index);
-
-        // Load the total storage buffer size from the UBO.
-        uint32_t array_index = size_index / 4;
-        auto* vec_expr = ctx.dst->IndexAccessor(
-            ctx.dst->MemberAccessor(get_ubo()->symbol, kBufferSizeMemberName), u32(array_index));
-        uint32_t vec_index = size_index % 4;
-        auto* total_storage_buffer_size = ctx.dst->IndexAccessor(vec_expr, u32(vec_index));
-
-        // Calculate actual array length
-        //                total_storage_buffer_size - array_offset
-        // array_length = ----------------------------------------
-        //                             array_stride
-        const ast::Expression* total_size = total_storage_buffer_size;
-        auto* storage_buffer_type = storage_buffer_sem->Type()->UnwrapRef();
-        const sem::Array* array_type = nullptr;
-        if (auto* str = storage_buffer_type->As<sem::Struct>()) {
-            // The variable is a struct, so subtract the byte offset of the array
-            // member.
-            auto* array_member_sem = str->Members().back();
-            array_type = array_member_sem->Type()->As<sem::Array>();
-            total_size = ctx.dst->Sub(total_storage_buffer_size, u32(array_member_sem->Offset()));
-        } else if (auto* arr = storage_buffer_type->As<sem::Array>()) {
-            array_type = arr;
-        } else {
-            TINT_ICE(Transform, ctx.dst->Diagnostics())
-                << "expected form of arrayLength argument to be &array_var or "
-                   "&struct_var.array_member";
-            return;
-        }
-        auto* array_length = ctx.dst->Div(total_size, u32(array_type->Stride()));
-
-        ctx.Replace(call_expr, array_length);
-    });
-
-    ctx.Clone();
-
-    outputs.Add<Result>(used_size_indices);
+Transform::ApplyResult ArrayLengthFromUniform::Apply(const Program* src,
+                                                     const DataMap& inputs,
+                                                     DataMap& outputs) const {
+    return State{src, inputs, outputs}.Run();
 }
 
 ArrayLengthFromUniform::Config::Config(sem::BindingPoint ubo_bp) : ubo_binding(ubo_bp) {}
diff --git a/src/tint/transform/array_length_from_uniform.h b/src/tint/transform/array_length_from_uniform.h
index 8bd6af5..507ea37 100644
--- a/src/tint/transform/array_length_from_uniform.h
+++ b/src/tint/transform/array_length_from_uniform.h
@@ -100,22 +100,12 @@
         std::unordered_set<uint32_t> used_size_indices;
     };
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
-    /// The PIMPL state for this transform
     struct State;
 };
 
diff --git a/src/tint/transform/array_length_from_uniform_test.cc b/src/tint/transform/array_length_from_uniform_test.cc
index 1058bf1..b5d9e77 100644
--- a/src/tint/transform/array_length_from_uniform_test.cc
+++ b/src/tint/transform/array_length_from_uniform_test.cc
@@ -28,7 +28,13 @@
 TEST_F(ArrayLengthFromUniformTest, ShouldRunEmptyModule) {
     auto* src = R"()";
 
-    EXPECT_FALSE(ShouldRun<ArrayLengthFromUniform>(src));
+    ArrayLengthFromUniform::Config cfg({0, 30u});
+    cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0);
+
+    DataMap data;
+    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
+
+    EXPECT_FALSE(ShouldRun<ArrayLengthFromUniform>(src, data));
 }
 
 TEST_F(ArrayLengthFromUniformTest, ShouldRunNoArrayLength) {
@@ -45,7 +51,13 @@
 }
 )";
 
-    EXPECT_FALSE(ShouldRun<ArrayLengthFromUniform>(src));
+    ArrayLengthFromUniform::Config cfg({0, 30u});
+    cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0);
+
+    DataMap data;
+    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
+
+    EXPECT_FALSE(ShouldRun<ArrayLengthFromUniform>(src, data));
 }
 
 TEST_F(ArrayLengthFromUniformTest, ShouldRunWithArrayLength) {
@@ -63,7 +75,13 @@
 }
 )";
 
-    EXPECT_TRUE(ShouldRun<ArrayLengthFromUniform>(src));
+    ArrayLengthFromUniform::Config cfg({0, 30u});
+    cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0);
+
+    DataMap data;
+    data.Add<ArrayLengthFromUniform::Config>(std::move(cfg));
+
+    EXPECT_TRUE(ShouldRun<ArrayLengthFromUniform>(src, data));
 }
 
 TEST_F(ArrayLengthFromUniformTest, Error_MissingTransformData) {
diff --git a/src/tint/transform/binding_remapper.cc b/src/tint/transform/binding_remapper.cc
index 798b228..0781355 100644
--- a/src/tint/transform/binding_remapper.cc
+++ b/src/tint/transform/binding_remapper.cc
@@ -40,19 +40,21 @@
 BindingRemapper::BindingRemapper() = default;
 BindingRemapper::~BindingRemapper() = default;
 
-bool BindingRemapper::ShouldRun(const Program*, const DataMap& inputs) const {
-    if (auto* remappings = inputs.Get<Remappings>()) {
-        return !remappings->binding_points.empty() || !remappings->access_controls.empty();
-    }
-    return false;
-}
+Transform::ApplyResult BindingRemapper::Apply(const Program* src,
+                                              const DataMap& inputs,
+                                              DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
 
-void BindingRemapper::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const {
     auto* remappings = inputs.Get<Remappings>();
     if (!remappings) {
-        ctx.dst->Diagnostics().add_error(
-            diag::System::Transform, "missing transform data for " + std::string(TypeInfo().name));
-        return;
+        b.Diagnostics().add_error(diag::System::Transform,
+                                  "missing transform data for " + std::string(TypeInfo().name));
+        return Program(std::move(b));
+    }
+
+    if (remappings->binding_points.empty() && remappings->access_controls.empty()) {
+        return SkipTransform;
     }
 
     // A set of post-remapped binding points that need to be decorated with a
@@ -62,11 +64,11 @@
     if (remappings->allow_collisions) {
         // Scan for binding point collisions generated by this transform.
         // Populate all collisions in the `add_collision_attr` set.
-        for (auto* func_ast : ctx.src->AST().Functions()) {
+        for (auto* func_ast : src->AST().Functions()) {
             if (!func_ast->IsEntryPoint()) {
                 continue;
             }
-            auto* func = ctx.src->Sem().Get(func_ast);
+            auto* func = src->Sem().Get(func_ast);
             std::unordered_map<sem::BindingPoint, int> binding_point_counts;
             for (auto* global : func->TransitivelyReferencedGlobals()) {
                 if (global->Declaration()->HasBindingPoint()) {
@@ -90,9 +92,9 @@
         }
     }
 
-    for (auto* var : ctx.src->AST().Globals<ast::Var>()) {
+    for (auto* var : src->AST().Globals<ast::Var>()) {
         if (var->HasBindingPoint()) {
-            auto* global_sem = ctx.src->Sem().Get<sem::GlobalVariable>(var);
+            auto* global_sem = src->Sem().Get<sem::GlobalVariable>(var);
 
             // The original binding point
             BindingPoint from = global_sem->BindingPoint();
@@ -106,8 +108,8 @@
             auto bp_it = remappings->binding_points.find(from);
             if (bp_it != remappings->binding_points.end()) {
                 BindingPoint to = bp_it->second;
-                auto* new_group = ctx.dst->Group(AInt(to.group));
-                auto* new_binding = ctx.dst->Binding(AInt(to.binding));
+                auto* new_group = b.Group(AInt(to.group));
+                auto* new_binding = b.Binding(AInt(to.binding));
 
                 auto* old_group = ast::GetAttribute<ast::GroupAttribute>(var->attributes);
                 auto* old_binding = ast::GetAttribute<ast::BindingAttribute>(var->attributes);
@@ -122,37 +124,37 @@
             if (ac_it != remappings->access_controls.end()) {
                 ast::Access ac = ac_it->second;
                 if (ac == ast::Access::kUndefined) {
-                    ctx.dst->Diagnostics().add_error(
+                    b.Diagnostics().add_error(
                         diag::System::Transform,
                         "invalid access mode (" + std::to_string(static_cast<uint32_t>(ac)) + ")");
-                    return;
+                    return Program(std::move(b));
                 }
-                auto* sem = ctx.src->Sem().Get(var);
+                auto* sem = src->Sem().Get(var);
                 if (sem->AddressSpace() != ast::AddressSpace::kStorage) {
-                    ctx.dst->Diagnostics().add_error(
+                    b.Diagnostics().add_error(
                         diag::System::Transform,
                         "cannot apply access control to variable with address space " +
                             std::string(utils::ToString(sem->AddressSpace())));
-                    return;
+                    return Program(std::move(b));
                 }
                 auto* ty = sem->Type()->UnwrapRef();
                 const ast::Type* inner_ty = CreateASTTypeFor(ctx, ty);
-                auto* new_var =
-                    ctx.dst->Var(ctx.Clone(var->source), ctx.Clone(var->symbol), inner_ty,
-                                 var->declared_address_space, ac, ctx.Clone(var->initializer),
-                                 ctx.Clone(var->attributes));
+                auto* new_var = b.Var(ctx.Clone(var->source), ctx.Clone(var->symbol), inner_ty,
+                                      var->declared_address_space, ac, ctx.Clone(var->initializer),
+                                      ctx.Clone(var->attributes));
                 ctx.Replace(var, new_var);
             }
 
             // Add `DisableValidationAttribute`s if required
             if (add_collision_attr.count(bp)) {
-                auto* attribute = ctx.dst->Disable(ast::DisabledValidation::kBindingPointCollision);
+                auto* attribute = b.Disable(ast::DisabledValidation::kBindingPointCollision);
                 ctx.InsertBefore(var->attributes, *var->attributes.begin(), attribute);
             }
         }
     }
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/binding_remapper.h b/src/tint/transform/binding_remapper.h
index 77fc5bc..b0efe0d 100644
--- a/src/tint/transform/binding_remapper.h
+++ b/src/tint/transform/binding_remapper.h
@@ -67,19 +67,10 @@
     BindingRemapper();
     ~BindingRemapper() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/binding_remapper_test.cc b/src/tint/transform/binding_remapper_test.cc
index 564a3a5..5bafb7e 100644
--- a/src/tint/transform/binding_remapper_test.cc
+++ b/src/tint/transform/binding_remapper_test.cc
@@ -23,12 +23,6 @@
 
 using BindingRemapperTest = TransformTest;
 
-TEST_F(BindingRemapperTest, ShouldRunNoRemappings) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<BindingRemapper>(src));
-}
-
 TEST_F(BindingRemapperTest, ShouldRunEmptyRemappings) {
     auto* src = R"()";
 
@@ -350,7 +344,7 @@
 }
 )";
 
-    auto* expect = src;
+    auto* expect = R"(error: missing transform data for tint::transform::BindingRemapper)";
 
     auto got = Run<BindingRemapper>(src);
 
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index ec00672..e80436d 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -29,7 +29,7 @@
 
 namespace tint::transform {
 
-/// The PIMPL state for the BuiltinPolyfill transform
+/// PIMPL state for the transform
 struct BuiltinPolyfill::State {
     /// Constructor
     /// @param c the CloneContext
@@ -556,6 +556,27 @@
         return name;
     }
 
+    /// Builds the polyfill function for the `quantizeToF16` builtin, by replacing the vector form
+    /// with scalar calls.
+    /// @param vec the vector type
+    /// @return the polyfill function name
+    Symbol quantizeToF16(const sem::Vector* vec) {
+        auto name = b.Symbols().New("tint_quantizeToF16");
+        utils::Vector<const ast::Expression*, 4> args;
+        for (uint32_t i = 0; i < vec->Width(); i++) {
+            args.Push(b.Call("quantizeToF16", b.IndexAccessor("v", u32(i))));
+        }
+        b.Func(name,
+               utils::Vector{
+                   b.Param("v", T(vec)),
+               },
+               T(vec),
+               utils::Vector{
+                   b.Return(b.Construct(T(vec), std::move(args))),
+               });
+        return name;
+    }
+
   private:
     /// @returns the AST type for the given sem type
     const ast::Type* T(const sem::Type* ty) const { return CreateASTTypeFor(ctx, ty); }
@@ -583,213 +604,165 @@
 
 BuiltinPolyfill::~BuiltinPolyfill() = default;
 
-bool BuiltinPolyfill::ShouldRun(const Program* program, const DataMap& data) const {
-    if (auto* cfg = data.Get<Config>()) {
-        auto builtins = cfg->builtins;
-        auto& sem = program->Sem();
-        for (auto* node : program->ASTNodes().Objects()) {
-            if (auto* call = sem.Get<sem::Call>(node)) {
-                if (auto* builtin = call->Target()->As<sem::Builtin>()) {
-                    if (call->Stage() == sem::EvaluationStage::kConstant) {
-                        continue;  // Don't polyfill @const expressions
-                    }
-                    switch (builtin->Type()) {
-                        case sem::BuiltinType::kAcosh:
-                            if (builtins.acosh != Level::kNone) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kAsinh:
-                            if (builtins.asinh) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kAtanh:
-                            if (builtins.atanh != Level::kNone) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kClamp:
-                            if (builtins.clamp_int) {
-                                auto& sig = builtin->Signature();
-                                return sig.parameters[0]->Type()->is_integer_scalar_or_vector();
-                            }
-                            break;
-                        case sem::BuiltinType::kCountLeadingZeros:
-                            if (builtins.count_leading_zeros) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kCountTrailingZeros:
-                            if (builtins.count_trailing_zeros) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kExtractBits:
-                            if (builtins.extract_bits != Level::kNone) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kFirstLeadingBit:
-                            if (builtins.first_leading_bit) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kFirstTrailingBit:
-                            if (builtins.first_trailing_bit) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kInsertBits:
-                            if (builtins.insert_bits != Level::kNone) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kSaturate:
-                            if (builtins.saturate) {
-                                return true;
-                            }
-                            break;
-                        case sem::BuiltinType::kTextureSampleBaseClampToEdge:
-                            if (builtins.texture_sample_base_clamp_to_edge_2d_f32) {
-                                auto& sig = builtin->Signature();
-                                auto* tex = sig.Parameter(sem::ParameterUsage::kTexture);
-                                if (auto* stex = tex->Type()->As<sem::SampledTexture>()) {
-                                    return stex->type()->Is<sem::F32>();
-                                }
-                            }
-                            break;
-                        default:
-                            break;
-                    }
-                }
-            }
-        }
-    }
-    return false;
-}
-
-void BuiltinPolyfill::Run(CloneContext& ctx, const DataMap& data, DataMap&) const {
+Transform::ApplyResult BuiltinPolyfill::Apply(const Program* src,
+                                              const DataMap& data,
+                                              DataMap&) const {
     auto* cfg = data.Get<Config>();
     if (!cfg) {
-        ctx.Clone();
-        return;
+        return SkipTransform;
     }
 
-    std::unordered_map<const sem::Builtin*, Symbol> polyfills;
+    auto& builtins = cfg->builtins;
 
-    ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::CallExpression* {
-        auto builtins = cfg->builtins;
-        State s{ctx, builtins};
-        if (auto* call = s.sem.Get<sem::Call>(expr)) {
-            if (auto* builtin = call->Target()->As<sem::Builtin>()) {
-                if (call->Stage() == sem::EvaluationStage::kConstant) {
-                    return nullptr;  // Don't polyfill @const expressions
-                }
-                Symbol polyfill;
-                switch (builtin->Type()) {
-                    case sem::BuiltinType::kAcosh:
-                        if (builtins.acosh != Level::kNone) {
-                            polyfill = utils::GetOrCreate(
-                                polyfills, builtin, [&] { return s.acosh(builtin->ReturnType()); });
+    utils::Hashmap<const sem::Builtin*, Symbol, 8> builtin_polyfills;
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+    State s{ctx, builtins};
+
+    bool made_changes = false;
+    for (auto* node : src->ASTNodes().Objects()) {
+        auto* expr = src->Sem().Get<sem::Expression>(node);
+        if (!expr || expr->Stage() == sem::EvaluationStage::kConstant) {
+            continue;  // Don't polyfill @const expressions
+        }
+
+        if (auto* call = expr->As<sem::Call>()) {
+            auto* builtin = call->Target()->As<sem::Builtin>();
+            if (!builtin) {
+                continue;
+            }
+            Symbol polyfill;
+            switch (builtin->Type()) {
+                case sem::BuiltinType::kAcosh:
+                    if (builtins.acosh != Level::kNone) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.acosh(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kAsinh:
+                    if (builtins.asinh) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.asinh(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kAtanh:
+                    if (builtins.atanh != Level::kNone) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.atanh(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kClamp:
+                    if (builtins.clamp_int) {
+                        auto& sig = builtin->Signature();
+                        if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
+                            polyfill = builtin_polyfills.GetOrCreate(
+                                builtin, [&] { return s.clampInteger(builtin->ReturnType()); });
                         }
-                        break;
-                    case sem::BuiltinType::kAsinh:
-                        if (builtins.asinh) {
-                            polyfill = utils::GetOrCreate(
-                                polyfills, builtin, [&] { return s.asinh(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kAtanh:
-                        if (builtins.atanh != Level::kNone) {
-                            polyfill = utils::GetOrCreate(
-                                polyfills, builtin, [&] { return s.atanh(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kClamp:
-                        if (builtins.clamp_int) {
-                            auto& sig = builtin->Signature();
-                            if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
-                                polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                    return s.clampInteger(builtin->ReturnType());
+                    }
+                    break;
+                case sem::BuiltinType::kCountLeadingZeros:
+                    if (builtins.count_leading_zeros) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.countLeadingZeros(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kCountTrailingZeros:
+                    if (builtins.count_trailing_zeros) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.countTrailingZeros(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kExtractBits:
+                    if (builtins.extract_bits != Level::kNone) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.extractBits(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kFirstLeadingBit:
+                    if (builtins.first_leading_bit) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.firstLeadingBit(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kFirstTrailingBit:
+                    if (builtins.first_trailing_bit) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.firstTrailingBit(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kInsertBits:
+                    if (builtins.insert_bits != Level::kNone) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.insertBits(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kSaturate:
+                    if (builtins.saturate) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.saturate(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kTextureSampleBaseClampToEdge:
+                    if (builtins.texture_sample_base_clamp_to_edge_2d_f32) {
+                        auto& sig = builtin->Signature();
+                        auto* tex = sig.Parameter(sem::ParameterUsage::kTexture);
+                        if (auto* stex = tex->Type()->As<sem::SampledTexture>()) {
+                            if (stex->type()->Is<sem::F32>()) {
+                                polyfill = builtin_polyfills.GetOrCreate(builtin, [&] {
+                                    return s.textureSampleBaseClampToEdge_2d_f32();
                                 });
                             }
                         }
-                        break;
-                    case sem::BuiltinType::kCountLeadingZeros:
-                        if (builtins.count_leading_zeros) {
-                            polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                return s.countLeadingZeros(builtin->ReturnType());
-                            });
+                    }
+                    break;
+                case sem::BuiltinType::kQuantizeToF16:
+                    if (builtins.quantize_to_vec_f16) {
+                        if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
+                            polyfill = builtin_polyfills.GetOrCreate(
+                                builtin, [&] { return s.quantizeToF16(vec); });
                         }
-                        break;
-                    case sem::BuiltinType::kCountTrailingZeros:
-                        if (builtins.count_trailing_zeros) {
-                            polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                return s.countTrailingZeros(builtin->ReturnType());
-                            });
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+
+            if (polyfill.IsValid()) {
+                auto* replacement = s.b.Call(polyfill, ctx.Clone(call->Declaration()->args));
+                ctx.Replace(call->Declaration(), replacement);
+                made_changes = true;
+            }
+        } else if (auto* bin_op = node->As<ast::BinaryExpression>()) {
+            switch (bin_op->op) {
+                case ast::BinaryOp::kShiftLeft:
+                case ast::BinaryOp::kShiftRight:
+                    if (builtins.bitshift_modulo) {
+                        auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
+                        auto* rhs_ty = src->TypeOf(bin_op->rhs)->UnwrapRef();
+                        auto* lhs_el_ty = sem::Type::DeepestElementOf(lhs_ty);
+                        const ast::Expression* mask = b.Expr(AInt(lhs_el_ty->Size() * 8 - 1));
+                        if (rhs_ty->Is<sem::Vector>()) {
+                            mask = b.Construct(CreateASTTypeFor(ctx, rhs_ty), mask);
                         }
-                        break;
-                    case sem::BuiltinType::kExtractBits:
-                        if (builtins.extract_bits != Level::kNone) {
-                            polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                return s.extractBits(builtin->ReturnType());
-                            });
-                        }
-                        break;
-                    case sem::BuiltinType::kFirstLeadingBit:
-                        if (builtins.first_leading_bit) {
-                            polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                return s.firstLeadingBit(builtin->ReturnType());
-                            });
-                        }
-                        break;
-                    case sem::BuiltinType::kFirstTrailingBit:
-                        if (builtins.first_trailing_bit) {
-                            polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                return s.firstTrailingBit(builtin->ReturnType());
-                            });
-                        }
-                        break;
-                    case sem::BuiltinType::kInsertBits:
-                        if (builtins.insert_bits != Level::kNone) {
-                            polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                return s.insertBits(builtin->ReturnType());
-                            });
-                        }
-                        break;
-                    case sem::BuiltinType::kSaturate:
-                        if (builtins.saturate) {
-                            polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                return s.saturate(builtin->ReturnType());
-                            });
-                        }
-                        break;
-                    case sem::BuiltinType::kTextureSampleBaseClampToEdge:
-                        if (builtins.texture_sample_base_clamp_to_edge_2d_f32) {
-                            auto& sig = builtin->Signature();
-                            auto* tex = sig.Parameter(sem::ParameterUsage::kTexture);
-                            if (auto* stex = tex->Type()->As<sem::SampledTexture>()) {
-                                if (stex->type()->Is<sem::F32>()) {
-                                    polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
-                                        return s.textureSampleBaseClampToEdge_2d_f32();
-                                    });
-                                }
-                            }
-                        }
-                        break;
-                    default:
-                        break;
-                }
-                if (polyfill.IsValid()) {
-                    return s.b.Call(polyfill, ctx.Clone(call->Declaration()->args));
-                }
+                        auto* mod = b.And(ctx.Clone(bin_op->rhs), mask);
+                        ctx.Replace(bin_op->rhs, mod);
+                        made_changes = true;
+                    }
+                    break;
+                default:
+                    break;
             }
         }
-        return nullptr;
-    });
+    }
+
+    if (!made_changes) {
+        return SkipTransform;
+    }
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 BuiltinPolyfill::Config::Config(const Builtins& b) : builtins(b) {}
diff --git a/src/tint/transform/builtin_polyfill.h b/src/tint/transform/builtin_polyfill.h
index d083252..7083aa7 100644
--- a/src/tint/transform/builtin_polyfill.h
+++ b/src/tint/transform/builtin_polyfill.h
@@ -47,6 +47,8 @@
         bool asinh = false;
         /// What level should `atanh` be polyfilled?
         Level atanh = Level::kNone;
+        /// Should the RHS of `<<` and `>>` be wrapped in a modulo bit-width of LHS?
+        bool bitshift_modulo = false;
         /// Should `clamp()` be polyfilled for integer values (scalar or vector)?
         bool clamp_int = false;
         /// Should `countLeadingZeros()` be polyfilled?
@@ -65,6 +67,9 @@
         bool saturate = false;
         /// Should `textureSampleBaseClampToEdge()` be polyfilled for texture_2d<f32> textures?
         bool texture_sample_base_clamp_to_edge_2d_f32 = false;
+        /// Should the vector form of `quantizeToF16()` be polyfilled with a scalar implementation?
+        /// See crbug.com/tint/1741
+        bool quantize_to_vec_f16 = false;
     };
 
     /// Config is consumed by the BuiltinPolyfill transform.
@@ -84,21 +89,13 @@
         const Builtins builtins;
     };
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-  protected:
+  private:
     struct State;
-
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/builtin_polyfill_test.cc b/src/tint/transform/builtin_polyfill_test.cc
index b938195..3b7a42c 100644
--- a/src/tint/transform/builtin_polyfill_test.cc
+++ b/src/tint/transform/builtin_polyfill_test.cc
@@ -399,6 +399,145 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// bitshiftModulo
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillBitshiftModulo() {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.bitshift_modulo = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shl_scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = 1i << v;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shl_vector) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = vec3(1i) << vec3(v);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shr_scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = 1i >> v;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shr_vector) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = vec3(1i) >> vec3(v);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
+}
+
+TEST_F(BuiltinPolyfillTest, BitshiftModulo_shl_scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = 1i << v;
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let v = 15u;
+  let r = (1i << (v & 31));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, BitshiftModulo_shl_vector) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = vec3(1i) << vec3(v);
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let v = 15u;
+  let r = (vec3(1i) << (vec3(v) & vec3<u32>(31)));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, BitshiftModulo_shr_scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = 1i >> v;
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let v = 15u;
+  let r = (1i >> (v & 31));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, BitshiftModulo_shr_vector) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = vec3(1i) >> vec3(v);
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let v = 15u;
+  let r = (vec3(1i) >> (vec3(v) & vec3<u32>(31)));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // clampInteger
 ////////////////////////////////////////////////////////////////////////////////
 DataMap polyfillClampInteger() {
@@ -1561,7 +1700,8 @@
 TEST_F(BuiltinPolyfillTest, DISABLED_InsertBits_ConstantExpression) {
     auto* src = R"(
 fn f() {
-  let r : i32 = insertBits(1234, 5678, 5u, 6u);
+  let v = 1234i;
+  let r : i32 = insertBits(v, 5678, 5u, 6u);
 }
 )";
 
@@ -1975,10 +2115,6 @@
 )";
 
     auto* expect = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@group(0) @binding(1) var s : sampler;
-
 fn tint_textureSampleBaseClampToEdge(t : texture_2d<f32>, s : sampler, coord : vec2<f32>) -> vec4<f32> {
   let dims = vec2<f32>(textureDimensions(t, 0));
   let half_texel = (vec2<f32>(0.5) / dims);
@@ -1986,6 +2122,10 @@
   return textureSampleLevel(t, s, clamped, 0);
 }
 
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
 fn f() {
   let r = tint_textureSampleBaseClampToEdge(t, s, vec2<f32>(0.5));
 }
@@ -1996,5 +2136,112 @@
     EXPECT_EQ(expect, str(got));
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// quantizeToF16
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillQuantizeToF16_2d_f32() {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.quantize_to_vec_f16 = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunQuantizeToF16_Scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 0.5;
+  quantizeToF16(0.5);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillQuantizeToF16_2d_f32()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunQuantizeToF16_Vector) {
+    auto* src = R"(
+fn f() {
+  let v = 0.5;
+  quantizeToF16(vec2(v));
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillQuantizeToF16_2d_f32()));
+}
+
+TEST_F(BuiltinPolyfillTest, QuantizeToF16_Vec2) {
+    auto* src = R"(
+fn f() {
+  let v = 0.5;
+  quantizeToF16(vec2(v));
+}
+)";
+
+    auto* expect = R"(
+fn tint_quantizeToF16(v : vec2<f32>) -> vec2<f32> {
+  return vec2<f32>(quantizeToF16(v[0u]), quantizeToF16(v[1u]));
+}
+
+fn f() {
+  let v = 0.5;
+  tint_quantizeToF16(vec2(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillQuantizeToF16_2d_f32());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, QuantizeToF16_Vec3) {
+    auto* src = R"(
+fn f() {
+  let v = 0.5;
+  quantizeToF16(vec3(v));
+}
+)";
+
+    auto* expect = R"(
+fn tint_quantizeToF16(v : vec3<f32>) -> vec3<f32> {
+  return vec3<f32>(quantizeToF16(v[0u]), quantizeToF16(v[1u]), quantizeToF16(v[2u]));
+}
+
+fn f() {
+  let v = 0.5;
+  tint_quantizeToF16(vec3(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillQuantizeToF16_2d_f32());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, QuantizeToF16_Vec4) {
+    auto* src = R"(
+fn f() {
+  let v = 0.5;
+  quantizeToF16(vec4(v));
+}
+)";
+
+    auto* expect = R"(
+fn tint_quantizeToF16(v : vec4<f32>) -> vec4<f32> {
+  return vec4<f32>(quantizeToF16(v[0u]), quantizeToF16(v[1u]), quantizeToF16(v[2u]), quantizeToF16(v[3u]));
+}
+
+fn f() {
+  let v = 0.5;
+  tint_quantizeToF16(vec4(v));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillQuantizeToF16_2d_f32());
+
+    EXPECT_EQ(expect, str(got));
+}
+
 }  // namespace
 }  // namespace tint::transform
diff --git a/src/tint/transform/calculate_array_length.cc b/src/tint/transform/calculate_array_length.cc
index 2ca5e54..9dcdd7b 100644
--- a/src/tint/transform/calculate_array_length.cc
+++ b/src/tint/transform/calculate_array_length.cc
@@ -40,6 +40,19 @@
 
 namespace {
 
+bool ShouldRun(const Program* program) {
+    for (auto* fn : program->AST().Functions()) {
+        if (auto* sem_fn = program->Sem().Get(fn)) {
+            for (auto* builtin : sem_fn->DirectlyCalledBuiltins()) {
+                if (builtin->Type() == sem::BuiltinType::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 {
@@ -73,21 +86,16 @@
 CalculateArrayLength::CalculateArrayLength() = default;
 CalculateArrayLength::~CalculateArrayLength() = default;
 
-bool CalculateArrayLength::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* fn : program->AST().Functions()) {
-        if (auto* sem_fn = program->Sem().Get(fn)) {
-            for (auto* builtin : sem_fn->DirectlyCalledBuiltins()) {
-                if (builtin->Type() == sem::BuiltinType::kArrayLength) {
-                    return true;
-                }
-            }
-        }
+Transform::ApplyResult CalculateArrayLength::Apply(const Program* src,
+                                                   const DataMap&,
+                                                   DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
     }
-    return false;
-}
 
-void CalculateArrayLength::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    auto& sem = ctx.src->Sem();
+    ProgramBuilder b;
+    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
@@ -95,24 +103,20 @@
     std::unordered_map<const sem::Reference*, Symbol> buffer_size_intrinsics;
     auto get_buffer_size_intrinsic = [&](const sem::Reference* buffer_type) {
         return utils::GetOrCreate(buffer_size_intrinsics, buffer_type, [&] {
-            auto name = ctx.dst->Sym();
+            auto name = b.Sym();
             auto* type = CreateASTTypeFor(ctx, buffer_type);
-            auto* disable_validation =
-                ctx.dst->Disable(ast::DisabledValidation::kFunctionParameter);
-            ctx.dst->AST().AddFunction(ctx.dst->create<ast::Function>(
+            auto* disable_validation = b.Disable(ast::DisabledValidation::kFunctionParameter);
+            b.AST().AddFunction(b.create<ast::Function>(
                 name,
                 utils::Vector{
-                    ctx.dst->Param("buffer",
-                                   ctx.dst->ty.pointer(type, buffer_type->AddressSpace(),
-                                                       buffer_type->Access()),
-                                   utils::Vector{disable_validation}),
-                    ctx.dst->Param("result", ctx.dst->ty.pointer(ctx.dst->ty.u32(),
-                                                                 ast::AddressSpace::kFunction)),
+                    b.Param("buffer",
+                            b.ty.pointer(type, buffer_type->AddressSpace(), buffer_type->Access()),
+                            utils::Vector{disable_validation}),
+                    b.Param("result", b.ty.pointer(b.ty.u32(), ast::AddressSpace::kFunction)),
                 },
-                ctx.dst->ty.void_(), nullptr,
+                b.ty.void_(), nullptr,
                 utils::Vector{
-                    ctx.dst->ASTNodes().Create<BufferSizeIntrinsic>(ctx.dst->ID(),
-                                                                    ctx.dst->AllocateNodeID()),
+                    b.ASTNodes().Create<BufferSizeIntrinsic>(b.ID(), b.AllocateNodeID()),
                 },
                 utils::Empty));
 
@@ -123,7 +127,7 @@
     std::unordered_map<ArrayUsage, Symbol, ArrayUsage::Hasher> array_length_by_usage;
 
     // Find all the arrayLength() calls...
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
+    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::Builtin>()) {
@@ -149,7 +153,7 @@
                     auto* arg = call_expr->args[0];
                     auto* address_of = arg->As<ast::UnaryOpExpression>();
                     if (!address_of || address_of->op != ast::UnaryOp::kAddressOf) {
-                        TINT_ICE(Transform, ctx.dst->Diagnostics())
+                        TINT_ICE(Transform, b.Diagnostics())
                             << "arrayLength() expected address-of, got " << arg->TypeInfo().name;
                     }
                     auto* storage_buffer_expr = address_of->expr;
@@ -158,7 +162,7 @@
                     }
                     auto* storage_buffer_sem = sem.Get<sem::VariableUser>(storage_buffer_expr);
                     if (!storage_buffer_sem) {
-                        TINT_ICE(Transform, ctx.dst->Diagnostics())
+                        TINT_ICE(Transform, b.Diagnostics())
                             << "expected form of arrayLength argument to be &array_var or "
                                "&struct_var.array_member";
                         break;
@@ -179,25 +183,24 @@
 
                             // Construct the variable that'll hold the result of
                             // RWByteAddressBuffer.GetDimensions()
-                            auto* buffer_size_result = ctx.dst->Decl(ctx.dst->Var(
-                                ctx.dst->Sym(), ctx.dst->ty.u32(), ctx.dst->Expr(0_u)));
+                            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 = ctx.dst->CallStmt(ctx.dst->Call(
+                            auto* call_get_dims = b.CallStmt(b.Call(
                                 // BufferSizeIntrinsic(X, ARGS...) is
                                 // translated to:
                                 //  X.GetDimensions(ARGS..) by the writer
-                                buffer_size, ctx.dst->AddressOf(ctx.Clone(storage_buffer_expr)),
-                                ctx.dst->AddressOf(
-                                    ctx.dst->Expr(buffer_size_result->variable->symbol))));
+                                buffer_size, b.AddressOf(ctx.Clone(storage_buffer_expr)),
+                                b.AddressOf(b.Expr(buffer_size_result->variable->symbol))));
 
                             // Calculate actual array length
                             //                total_storage_buffer_size - array_offset
                             // array_length = ----------------------------------------
                             //                             array_stride
-                            auto name = ctx.dst->Sym();
+                            auto name = b.Sym();
                             const ast::Expression* total_size =
-                                ctx.dst->Expr(buffer_size_result->variable);
+                                b.Expr(buffer_size_result->variable);
 
                             const sem::Array* array_type = Switch(
                                 storage_buffer_type->StoreType(),
@@ -205,23 +208,21 @@
                                     // The variable is a struct, so subtract the byte offset of
                                     // the array member.
                                     auto* array_member_sem = str->Members().back();
-                                    total_size =
-                                        ctx.dst->Sub(total_size, u32(array_member_sem->Offset()));
+                                    total_size = b.Sub(total_size, u32(array_member_sem->Offset()));
                                     return array_member_sem->Type()->As<sem::Array>();
                                 },
                                 [&](const sem::Array* arr) { return arr; });
 
                             if (!array_type) {
-                                TINT_ICE(Transform, ctx.dst->Diagnostics())
+                                TINT_ICE(Transform, b.Diagnostics())
                                     << "expected form of arrayLength argument to be "
                                        "&array_var or &struct_var.array_member";
                                 return name;
                             }
 
                             uint32_t array_stride = array_type->Size();
-                            auto* array_length_var = ctx.dst->Decl(
-                                ctx.dst->Let(name, ctx.dst->ty.u32(),
-                                             ctx.dst->Div(total_size, u32(array_stride))));
+                            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],
@@ -234,13 +235,14 @@
                         });
 
                     // Replace the call to arrayLength() with the array length variable
-                    ctx.Replace(call_expr, ctx.dst->Expr(array_length));
+                    ctx.Replace(call_expr, b.Expr(array_length));
                 }
             }
         }
     }
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/calculate_array_length.h b/src/tint/transform/calculate_array_length.h
index 8db8dcc..e5714a8 100644
--- a/src/tint/transform/calculate_array_length.h
+++ b/src/tint/transform/calculate_array_length.h
@@ -59,19 +59,10 @@
     /// Destructor
     ~CalculateArrayLength() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index b990965..8a14fb7 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -123,7 +123,7 @@
 
 }  // namespace
 
-/// State holds the current transform state for a single entry point.
+/// PIMPL state for the transform
 struct CanonicalizeEntryPointIO::State {
     /// OutputValue represents a shader result that the wrapper function produces.
     struct OutputValue {
@@ -770,17 +770,22 @@
     }
 };
 
-void CanonicalizeEntryPointIO::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const {
+Transform::ApplyResult CanonicalizeEntryPointIO::Apply(const Program* src,
+                                                       const DataMap& inputs,
+                                                       DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     auto* cfg = inputs.Get<Config>();
     if (cfg == nullptr) {
-        ctx.dst->Diagnostics().add_error(
-            diag::System::Transform, "missing transform data for " + std::string(TypeInfo().name));
-        return;
+        b.Diagnostics().add_error(diag::System::Transform,
+                                  "missing transform data for " + std::string(TypeInfo().name));
+        return Program(std::move(b));
     }
 
     // Remove entry point IO attributes from struct declarations.
     // New structures will be created for each entry point, as necessary.
-    for (auto* ty : ctx.src->AST().TypeDecls()) {
+    for (auto* ty : src->AST().TypeDecls()) {
         if (auto* struct_ty = ty->As<ast::Struct>()) {
             for (auto* member : struct_ty->members) {
                 for (auto* attr : member->attributes) {
@@ -792,7 +797,7 @@
         }
     }
 
-    for (auto* func_ast : ctx.src->AST().Functions()) {
+    for (auto* func_ast : src->AST().Functions()) {
         if (!func_ast->IsEntryPoint()) {
             continue;
         }
@@ -802,6 +807,7 @@
     }
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 CanonicalizeEntryPointIO::Config::Config(ShaderStyle style,
diff --git a/src/tint/transform/canonicalize_entry_point_io.h b/src/tint/transform/canonicalize_entry_point_io.h
index 95f8b19..fbfed5e 100644
--- a/src/tint/transform/canonicalize_entry_point_io.h
+++ b/src/tint/transform/canonicalize_entry_point_io.h
@@ -127,15 +127,12 @@
     CanonicalizeEntryPointIO();
     ~CanonicalizeEntryPointIO() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
+  private:
     struct State;
 };
 
diff --git a/src/tint/transform/clamp_frag_depth.cc b/src/tint/transform/clamp_frag_depth.cc
index e67dda3..4551925 100644
--- a/src/tint/transform/clamp_frag_depth.cc
+++ b/src/tint/transform/clamp_frag_depth.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/transform/clamp_frag_depth.h"
 
- #include <utility>
+#include <utility>
 
 #include "src/tint/ast/attribute.h"
 #include "src/tint/ast/builtin_attribute.h"
@@ -64,12 +64,7 @@
     return false;
 }
 
-}  // anonymous namespace
-
-ClampFragDepth::ClampFragDepth() = default;
-ClampFragDepth::~ClampFragDepth() = default;
-
-bool ClampFragDepth::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     auto& sem = program->Sem();
 
     for (auto* fn : program->AST().Functions()) {
@@ -82,22 +77,33 @@
     return false;
 }
 
-void ClampFragDepth::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+}  // anonymous namespace
+
+ClampFragDepth::ClampFragDepth() = default;
+ClampFragDepth::~ClampFragDepth() = default;
+
+Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&, DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     // Abort on any use of push constants in the module.
-    for (auto* global : ctx.src->AST().GlobalVariables()) {
+    for (auto* global : src->AST().GlobalVariables()) {
         if (auto* var = global->As<ast::Var>()) {
             if (var->declared_address_space == ast::AddressSpace::kPushConstant) {
-                TINT_ICE(Transform, ctx.dst->Diagnostics())
+                TINT_ICE(Transform, b.Diagnostics())
                     << "ClampFragDepth doesn't know how to handle module that already use push "
                        "constants.";
-                return;
+                return Program(std::move(b));
             }
         }
     }
 
-    auto& b = *ctx.dst;
-    auto& sem = ctx.src->Sem();
-    auto& sym = ctx.src->Symbols();
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    auto& sem = src->Sem();
+    auto& sym = src->Symbols();
 
     // At least one entry-point needs clamping. Add the following to the module:
     //
@@ -197,6 +203,7 @@
     });
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/clamp_frag_depth.h b/src/tint/transform/clamp_frag_depth.h
index 3b15f11..1e9d0d6 100644
--- a/src/tint/transform/clamp_frag_depth.h
+++ b/src/tint/transform/clamp_frag_depth.h
@@ -61,19 +61,10 @@
     /// Destructor
     ~ClampFragDepth() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/combine_samplers.cc b/src/tint/transform/combine_samplers.cc
index 97650ad..e7286d4 100644
--- a/src/tint/transform/combine_samplers.cc
+++ b/src/tint/transform/combine_samplers.cc
@@ -47,10 +47,14 @@
 CombineSamplers::BindingInfo::BindingInfo(const BindingInfo& other) = default;
 CombineSamplers::BindingInfo::~BindingInfo() = default;
 
-/// The PIMPL state for the CombineSamplers transform
+/// PIMPL state for the transform
 struct CombineSamplers::State {
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
     /// The clone context
-    CloneContext& ctx;
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
 
     /// The binding info
     const BindingInfo* binding_info;
@@ -88,9 +92,9 @@
     }
 
     /// Constructor
-    /// @param context the clone context
+    /// @param program the source program
     /// @param info the binding map information
-    State(CloneContext& context, const BindingInfo* info) : ctx(context), binding_info(info) {}
+    State(const Program* program, const BindingInfo* info) : src(program), binding_info(info) {}
 
     /// Creates a combined sampler global variables.
     /// (Note this is actually a Texture node at the AST level, but it will be
@@ -145,8 +149,9 @@
         }
     }
 
-    /// Performs the transformation
-    void Run() {
+    /// Runs the transform
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
         auto& sem = ctx.src->Sem();
 
         // Remove all texture and sampler global variables. These will be replaced
@@ -169,14 +174,14 @@
 
         // Rewrite all function signatures to use combined samplers, and remove
         // separate textures & samplers. Create new combined globals where found.
-        ctx.ReplaceAll([&](const ast::Function* src) -> const ast::Function* {
-            if (auto* func = sem.Get(src)) {
-                auto pairs = func->TextureSamplerPairs();
+        ctx.ReplaceAll([&](const ast::Function* ast_fn) -> const ast::Function* {
+            if (auto* fn = sem.Get(ast_fn)) {
+                auto pairs = fn->TextureSamplerPairs();
                 if (pairs.IsEmpty()) {
                     return nullptr;
                 }
                 utils::Vector<const ast::Parameter*, 8> params;
-                for (auto pair : func->TextureSamplerPairs()) {
+                for (auto pair : fn->TextureSamplerPairs()) {
                     const sem::Variable* texture_var = pair.first;
                     const sem::Variable* sampler_var = pair.second;
                     std::string name =
@@ -197,23 +202,23 @@
                         auto* type = CreateCombinedASTTypeFor(texture_var, sampler_var);
                         auto* var = ctx.dst->Param(ctx.dst->Symbols().New(name), type);
                         params.Push(var);
-                        function_combined_texture_samplers_[func][pair] = var;
+                        function_combined_texture_samplers_[fn][pair] = var;
                     }
                 }
                 // Filter out separate textures and samplers from the original
                 // function signature.
-                for (auto* var : src->params) {
-                    if (!sem.Get(var->type)->IsAnyOf<sem::Texture, sem::Sampler>()) {
-                        params.Push(ctx.Clone(var));
+                for (auto* param : fn->Parameters()) {
+                    if (!param->Type()->IsAnyOf<sem::Texture, sem::Sampler>()) {
+                        params.Push(ctx.Clone(param->Declaration()));
                     }
                 }
                 // Create a new function signature that differs only in the parameter
                 // list.
-                auto symbol = ctx.Clone(src->symbol);
-                auto* return_type = ctx.Clone(src->return_type);
-                auto* body = ctx.Clone(src->body);
-                auto attributes = ctx.Clone(src->attributes);
-                auto return_type_attributes = ctx.Clone(src->return_type_attributes);
+                auto symbol = ctx.Clone(ast_fn->symbol);
+                auto* return_type = ctx.Clone(ast_fn->return_type);
+                auto* body = ctx.Clone(ast_fn->body);
+                auto attributes = ctx.Clone(ast_fn->attributes);
+                auto return_type_attributes = ctx.Clone(ast_fn->return_type_attributes);
                 return ctx.dst->create<ast::Function>(symbol, params, return_type, body,
                                                       std::move(attributes),
                                                       std::move(return_type_attributes));
@@ -327,6 +332,7 @@
         });
 
         ctx.Clone();
+        return Program(std::move(b));
     }
 };
 
@@ -334,15 +340,18 @@
 
 CombineSamplers::~CombineSamplers() = default;
 
-void CombineSamplers::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const {
+Transform::ApplyResult CombineSamplers::Apply(const Program* src,
+                                              const DataMap& inputs,
+                                              DataMap&) const {
     auto* binding_info = inputs.Get<BindingInfo>();
     if (!binding_info) {
-        ctx.dst->Diagnostics().add_error(
-            diag::System::Transform, "missing transform data for " + std::string(TypeInfo().name));
-        return;
+        ProgramBuilder b;
+        b.Diagnostics().add_error(diag::System::Transform,
+                                  "missing transform data for " + std::string(TypeInfo().name));
+        return Program(std::move(b));
     }
 
-    State(ctx, binding_info).Run();
+    return State(src, binding_info).Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/combine_samplers.h b/src/tint/transform/combine_samplers.h
index 8dfc098..6834abe 100644
--- a/src/tint/transform/combine_samplers.h
+++ b/src/tint/transform/combine_samplers.h
@@ -88,17 +88,13 @@
     /// Destructor
     ~CombineSamplers() override;
 
-  protected:
-    /// The PIMPL state for this transform
-    struct State;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+  private:
+    struct State;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 68324af..046583e 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -47,6 +47,18 @@
 
 namespace {
 
+bool ShouldRun(const Program* program) {
+    for (auto* decl : program->AST().GlobalDeclarations()) {
+        if (auto* var = program->Sem().Get<sem::Variable>(decl)) {
+            if (var->AddressSpace() == ast::AddressSpace::kStorage ||
+                var->AddressSpace() == ast::AddressSpace::kUniform) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 /// Offset is a simple ast::Expression builder interface, used to build byte
 /// offsets for storage and uniform buffer accesses.
 struct Offset : Castable<Offset> {
@@ -291,7 +303,7 @@
 
 }  // namespace
 
-/// State holds the current transform state
+/// PIMPL state for the transform
 struct DecomposeMemoryAccess::State {
     /// The clone context
     CloneContext& ctx;
@@ -477,7 +489,7 @@
                         // * 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(Transform, ctx.dst->Diagnostics())
+                        TINT_ICE(Transform, b.Diagnostics())
                             << "unexpected non-constant array count";
                         arr_cnt = 1;
                     }
@@ -578,7 +590,7 @@
                                 // * 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(Transform, ctx.dst->Diagnostics())
+                                TINT_ICE(Transform, b.Diagnostics())
                                     << "unexpected non-constant array count";
                                 arr_cnt = 1;
                             }
@@ -808,21 +820,16 @@
 DecomposeMemoryAccess::DecomposeMemoryAccess() = default;
 DecomposeMemoryAccess::~DecomposeMemoryAccess() = default;
 
-bool DecomposeMemoryAccess::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* decl : program->AST().GlobalDeclarations()) {
-        if (auto* var = program->Sem().Get<sem::Variable>(decl)) {
-            if (var->AddressSpace() == ast::AddressSpace::kStorage ||
-                var->AddressSpace() == ast::AddressSpace::kUniform) {
-                return true;
-            }
-        }
+Transform::ApplyResult DecomposeMemoryAccess::Apply(const Program* src,
+                                                    const DataMap&,
+                                                    DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
     }
-    return false;
-}
 
-void DecomposeMemoryAccess::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    auto& sem = ctx.src->Sem();
-
+    auto& sem = src->Sem();
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
     State state(ctx);
 
     // Scan the AST nodes for storage and uniform buffer accesses. Complex
@@ -833,7 +840,7 @@
     // 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 : ctx.src->ASTNodes().Objects()) {
+    for (auto* node : src->ASTNodes().Objects()) {
         if (auto* ident = node->As<ast::IdentifierExpression>()) {
             // X
             if (auto* var = sem.Get<sem::VariableUser>(ident)) {
@@ -1001,6 +1008,7 @@
     }
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/decompose_memory_access.h b/src/tint/transform/decompose_memory_access.h
index 2e92a3a..21c196b 100644
--- a/src/tint/transform/decompose_memory_access.h
+++ b/src/tint/transform/decompose_memory_access.h
@@ -108,20 +108,12 @@
     /// Destructor
     ~DecomposeMemoryAccess() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
-
+  private:
     struct State;
 };
 
diff --git a/src/tint/transform/decompose_strided_array.cc b/src/tint/transform/decompose_strided_array.cc
index e9f51a5..73a6629 100644
--- a/src/tint/transform/decompose_strided_array.cc
+++ b/src/tint/transform/decompose_strided_array.cc
@@ -34,13 +34,7 @@
 
 using DecomposedArrays = std::unordered_map<const sem::Array*, Symbol>;
 
-}  // namespace
-
-DecomposeStridedArray::DecomposeStridedArray() = default;
-
-DecomposeStridedArray::~DecomposeStridedArray() = default;
-
-bool DecomposeStridedArray::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     for (auto* node : program->ASTNodes().Objects()) {
         if (auto* ast = node->As<ast::Array>()) {
             if (ast::GetAttribute<ast::StrideAttribute>(ast->attributes)) {
@@ -51,8 +45,22 @@
     return false;
 }
 
-void DecomposeStridedArray::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    const auto& sem = ctx.src->Sem();
+}  // namespace
+
+DecomposeStridedArray::DecomposeStridedArray() = default;
+
+DecomposeStridedArray::~DecomposeStridedArray() = default;
+
+Transform::ApplyResult DecomposeStridedArray::Apply(const Program* src,
+                                                    const DataMap&,
+                                                    DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+    const auto& sem = src->Sem();
 
     static constexpr const char* kMemberName = "el";
 
@@ -69,23 +77,23 @@
         if (auto* arr = sem.Get(ast)) {
             if (!arr->IsStrideImplicit()) {
                 auto el_ty = utils::GetOrCreate(decomposed, arr, [&] {
-                    auto name = ctx.dst->Symbols().New("strided_arr");
+                    auto name = b.Symbols().New("strided_arr");
                     auto* member_ty = ctx.Clone(ast->type);
-                    auto* member = ctx.dst->Member(kMemberName, member_ty,
-                                                   utils::Vector{
-                                                       ctx.dst->MemberSize(AInt(arr->Stride())),
-                                                   });
-                    ctx.dst->Structure(name, utils::Vector{member});
+                    auto* member = b.Member(kMemberName, member_ty,
+                                            utils::Vector{
+                                                b.MemberSize(AInt(arr->Stride())),
+                                            });
+                    b.Structure(name, utils::Vector{member});
                     return name;
                 });
                 auto* count = ctx.Clone(ast->count);
-                return ctx.dst->ty.array(ctx.dst->ty.type_name(el_ty), count);
+                return b.ty.array(b.ty.type_name(el_ty), count);
             }
             if (ast::GetAttribute<ast::StrideAttribute>(ast->attributes)) {
                 // Strip the @stride attribute
                 auto* ty = ctx.Clone(ast->type);
                 auto* count = ctx.Clone(ast->count);
-                return ctx.dst->ty.array(ty, count);
+                return b.ty.array(ty, count);
             }
         }
         return nullptr;
@@ -96,11 +104,11 @@
     // to insert an additional member accessor for the single structure field.
     // Example: `arr[i]` -> `arr[i].el`
     ctx.ReplaceAll([&](const ast::IndexAccessorExpression* idx) -> const ast::Expression* {
-        if (auto* ty = ctx.src->TypeOf(idx->object)) {
+        if (auto* ty = src->TypeOf(idx->object)) {
             if (auto* arr = ty->UnwrapRef()->As<sem::Array>()) {
                 if (!arr->IsStrideImplicit()) {
                     auto* expr = ctx.CloneWithoutTransform(idx);
-                    return ctx.dst->MemberAccessor(expr, kMemberName);
+                    return b.MemberAccessor(expr, kMemberName);
                 }
             }
         }
@@ -136,21 +144,23 @@
                         if (auto it = decomposed.find(arr); it != decomposed.end()) {
                             args.Reserve(expr->args.Length());
                             for (auto* arg : expr->args) {
-                                args.Push(ctx.dst->Call(it->second, ctx.Clone(arg)));
+                                args.Push(b.Call(it->second, ctx.Clone(arg)));
                             }
                         } else {
                             args = ctx.Clone(expr->args);
                         }
 
-                        return target.type ? ctx.dst->Construct(target.type, std::move(args))
-                                           : ctx.dst->Call(target.name, std::move(args));
+                        return target.type ? b.Construct(target.type, std::move(args))
+                                           : b.Call(target.name, std::move(args));
                     }
                 }
             }
         }
         return nullptr;
     });
+
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/decompose_strided_array.h b/src/tint/transform/decompose_strided_array.h
index 5dbaaa5..9555a9a 100644
--- a/src/tint/transform/decompose_strided_array.h
+++ b/src/tint/transform/decompose_strided_array.h
@@ -35,19 +35,10 @@
     /// Destructor
     ~DecomposeStridedArray() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/decompose_strided_matrix.cc b/src/tint/transform/decompose_strided_matrix.cc
index 91aed43..5494ca2 100644
--- a/src/tint/transform/decompose_strided_matrix.cc
+++ b/src/tint/transform/decompose_strided_matrix.cc
@@ -53,24 +53,25 @@
     };
 };
 
-/// Return type of the callback function of GatherCustomStrideMatrixMembers
-enum GatherResult { kContinue, kStop };
+}  // namespace
 
-/// GatherCustomStrideMatrixMembers scans `program` for all matrix members of
-/// storage and uniform structs, which are of a matrix type, and have a custom
-/// matrix stride attribute. For each matrix member found, `callback` is called.
-/// `callback` is a function with the signature:
-///      GatherResult(const sem::StructMember* member,
-///                   sem::Matrix* matrix,
-///                   uint32_t stride)
-/// If `callback` return GatherResult::kStop, then the scanning will immediately
-/// terminate, and GatherCustomStrideMatrixMembers() will return, otherwise
-/// scanning will continue.
-template <typename F>
-void GatherCustomStrideMatrixMembers(const Program* program, F&& callback) {
-    for (auto* node : program->ASTNodes().Objects()) {
+DecomposeStridedMatrix::DecomposeStridedMatrix() = default;
+
+DecomposeStridedMatrix::~DecomposeStridedMatrix() = default;
+
+Transform::ApplyResult DecomposeStridedMatrix::Apply(const Program* src,
+                                                     const DataMap&,
+                                                     DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
+    // Scan the program for all storage and uniform structure matrix members with
+    // a custom stride attribute. Replace these matrices with an equivalent array,
+    // and populate the `decomposed` map with the members that have been replaced.
+    utils::Hashmap<const ast::StructMember*, MatrixInfo, 8> decomposed;
+    for (auto* node : src->ASTNodes().Objects()) {
         if (auto* str = node->As<ast::Struct>()) {
-            auto* str_ty = program->Sem().Get(str);
+            auto* str_ty = src->Sem().Get(str);
             if (!str_ty->UsedAs(ast::AddressSpace::kUniform) &&
                 !str_ty->UsedAs(ast::AddressSpace::kStorage)) {
                 continue;
@@ -89,46 +90,20 @@
                 if (matrix->ColumnStride() == stride) {
                     continue;
                 }
-                if (callback(member, matrix, stride) == GatherResult::kStop) {
-                    return;
-                }
+                // We've got ourselves a struct member of a matrix type with a custom
+                // stride. Replace this with an array of column vectors.
+                MatrixInfo info{stride, matrix};
+                auto* replacement =
+                    b.Member(member->Offset(), ctx.Clone(member->Name()), info.array(ctx.dst));
+                ctx.Replace(member->Declaration(), replacement);
+                decomposed.Add(member->Declaration(), info);
             }
         }
     }
-}
 
-}  // namespace
-
-DecomposeStridedMatrix::DecomposeStridedMatrix() = default;
-
-DecomposeStridedMatrix::~DecomposeStridedMatrix() = default;
-
-bool DecomposeStridedMatrix::ShouldRun(const Program* program, const DataMap&) const {
-    bool should_run = false;
-    GatherCustomStrideMatrixMembers(program,
-                                    [&](const sem::StructMember*, const sem::Matrix*, uint32_t) {
-                                        should_run = true;
-                                        return GatherResult::kStop;
-                                    });
-    return should_run;
-}
-
-void DecomposeStridedMatrix::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    // Scan the program for all storage and uniform structure matrix members with
-    // a custom stride attribute. Replace these matrices with an equivalent array,
-    // and populate the `decomposed` map with the members that have been replaced.
-    std::unordered_map<const ast::StructMember*, MatrixInfo> decomposed;
-    GatherCustomStrideMatrixMembers(
-        ctx.src, [&](const sem::StructMember* member, const sem::Matrix* matrix, uint32_t stride) {
-            // We've got ourselves a struct member of a matrix type with a custom
-            // stride. Replace this with an array of column vectors.
-            MatrixInfo info{stride, matrix};
-            auto* replacement =
-                ctx.dst->Member(member->Offset(), ctx.Clone(member->Name()), info.array(ctx.dst));
-            ctx.Replace(member->Declaration(), replacement);
-            decomposed.emplace(member->Declaration(), info);
-            return GatherResult::kContinue;
-        });
+    if (decomposed.IsEmpty()) {
+        return SkipTransform;
+    }
 
     // For all expressions where a single matrix column vector was indexed, we can
     // preserve these without calling conversion functions.
@@ -136,12 +111,11 @@
     //   ssbo.mat[2] -> ssbo.mat[2]
     ctx.ReplaceAll(
         [&](const ast::IndexAccessorExpression* expr) -> const ast::IndexAccessorExpression* {
-            if (auto* access = ctx.src->Sem().Get<sem::StructMemberAccess>(expr->object)) {
-                auto it = decomposed.find(access->Member()->Declaration());
-                if (it != decomposed.end()) {
+            if (auto* access = src->Sem().Get<sem::StructMemberAccess>(expr->object)) {
+                if (decomposed.Contains(access->Member()->Declaration())) {
                     auto* obj = ctx.CloneWithoutTransform(expr->object);
                     auto* idx = ctx.Clone(expr->index);
-                    return ctx.dst->IndexAccessor(obj, idx);
+                    return b.IndexAccessor(obj, idx);
                 }
             }
             return nullptr;
@@ -154,39 +128,36 @@
     //   ssbo.mat = mat_to_arr(m)
     std::unordered_map<MatrixInfo, Symbol, MatrixInfo::Hasher> mat_to_arr;
     ctx.ReplaceAll([&](const ast::AssignmentStatement* stmt) -> const ast::Statement* {
-        if (auto* access = ctx.src->Sem().Get<sem::StructMemberAccess>(stmt->lhs)) {
-            auto it = decomposed.find(access->Member()->Declaration());
-            if (it == decomposed.end()) {
-                return nullptr;
+        if (auto* access = src->Sem().Get<sem::StructMemberAccess>(stmt->lhs)) {
+            if (auto* info = decomposed.Find(access->Member()->Declaration())) {
+                auto fn = utils::GetOrCreate(mat_to_arr, *info, [&] {
+                    auto name =
+                        b.Symbols().New("mat" + std::to_string(info->matrix->columns()) + "x" +
+                                        std::to_string(info->matrix->rows()) + "_stride_" +
+                                        std::to_string(info->stride) + "_to_arr");
+
+                    auto matrix = [&] { return CreateASTTypeFor(ctx, info->matrix); };
+                    auto array = [&] { return info->array(ctx.dst); };
+
+                    auto mat = b.Sym("m");
+                    utils::Vector<const ast::Expression*, 4> columns;
+                    for (uint32_t i = 0; i < static_cast<uint32_t>(info->matrix->columns()); i++) {
+                        columns.Push(b.IndexAccessor(mat, u32(i)));
+                    }
+                    b.Func(name,
+                           utils::Vector{
+                               b.Param(mat, matrix()),
+                           },
+                           array(),
+                           utils::Vector{
+                               b.Return(b.Construct(array(), columns)),
+                           });
+                    return name;
+                });
+                auto* lhs = ctx.CloneWithoutTransform(stmt->lhs);
+                auto* rhs = b.Call(fn, ctx.Clone(stmt->rhs));
+                return b.Assign(lhs, rhs);
             }
-            MatrixInfo info = it->second;
-            auto fn = utils::GetOrCreate(mat_to_arr, info, [&] {
-                auto name =
-                    ctx.dst->Symbols().New("mat" + std::to_string(info.matrix->columns()) + "x" +
-                                           std::to_string(info.matrix->rows()) + "_stride_" +
-                                           std::to_string(info.stride) + "_to_arr");
-
-                auto matrix = [&] { return CreateASTTypeFor(ctx, info.matrix); };
-                auto array = [&] { return info.array(ctx.dst); };
-
-                auto mat = ctx.dst->Sym("m");
-                utils::Vector<const ast::Expression*, 4> columns;
-                for (uint32_t i = 0; i < static_cast<uint32_t>(info.matrix->columns()); i++) {
-                    columns.Push(ctx.dst->IndexAccessor(mat, u32(i)));
-                }
-                ctx.dst->Func(name,
-                              utils::Vector{
-                                  ctx.dst->Param(mat, matrix()),
-                              },
-                              array(),
-                              utils::Vector{
-                                  ctx.dst->Return(ctx.dst->Construct(array(), columns)),
-                              });
-                return name;
-            });
-            auto* lhs = ctx.CloneWithoutTransform(stmt->lhs);
-            auto* rhs = ctx.dst->Call(fn, ctx.Clone(stmt->rhs));
-            return ctx.dst->Assign(lhs, rhs);
         }
         return nullptr;
     });
@@ -196,41 +167,40 @@
     //   m = arr_to_mat(ssbo.mat)
     std::unordered_map<MatrixInfo, Symbol, MatrixInfo::Hasher> arr_to_mat;
     ctx.ReplaceAll([&](const ast::MemberAccessorExpression* expr) -> const ast::Expression* {
-        if (auto* access = ctx.src->Sem().Get<sem::StructMemberAccess>(expr)) {
-            auto it = decomposed.find(access->Member()->Declaration());
-            if (it == decomposed.end()) {
-                return nullptr;
+        if (auto* access = src->Sem().Get<sem::StructMemberAccess>(expr)) {
+            if (auto* info = decomposed.Find(access->Member()->Declaration())) {
+                auto fn = utils::GetOrCreate(arr_to_mat, *info, [&] {
+                    auto name =
+                        b.Symbols().New("arr_to_mat" + std::to_string(info->matrix->columns()) +
+                                        "x" + std::to_string(info->matrix->rows()) + "_stride_" +
+                                        std::to_string(info->stride));
+
+                    auto matrix = [&] { return CreateASTTypeFor(ctx, info->matrix); };
+                    auto array = [&] { return info->array(ctx.dst); };
+
+                    auto arr = b.Sym("arr");
+                    utils::Vector<const ast::Expression*, 4> columns;
+                    for (uint32_t i = 0; i < static_cast<uint32_t>(info->matrix->columns()); i++) {
+                        columns.Push(b.IndexAccessor(arr, u32(i)));
+                    }
+                    b.Func(name,
+                           utils::Vector{
+                               b.Param(arr, array()),
+                           },
+                           matrix(),
+                           utils::Vector{
+                               b.Return(b.Construct(matrix(), columns)),
+                           });
+                    return name;
+                });
+                return b.Call(fn, ctx.CloneWithoutTransform(expr));
             }
-            MatrixInfo info = it->second;
-            auto fn = utils::GetOrCreate(arr_to_mat, info, [&] {
-                auto name = ctx.dst->Symbols().New(
-                    "arr_to_mat" + std::to_string(info.matrix->columns()) + "x" +
-                    std::to_string(info.matrix->rows()) + "_stride_" + std::to_string(info.stride));
-
-                auto matrix = [&] { return CreateASTTypeFor(ctx, info.matrix); };
-                auto array = [&] { return info.array(ctx.dst); };
-
-                auto arr = ctx.dst->Sym("arr");
-                utils::Vector<const ast::Expression*, 4> columns;
-                for (uint32_t i = 0; i < static_cast<uint32_t>(info.matrix->columns()); i++) {
-                    columns.Push(ctx.dst->IndexAccessor(arr, u32(i)));
-                }
-                ctx.dst->Func(name,
-                              utils::Vector{
-                                  ctx.dst->Param(arr, array()),
-                              },
-                              matrix(),
-                              utils::Vector{
-                                  ctx.dst->Return(ctx.dst->Construct(matrix(), columns)),
-                              });
-                return name;
-            });
-            return ctx.dst->Call(fn, ctx.CloneWithoutTransform(expr));
         }
         return nullptr;
     });
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/decompose_strided_matrix.h b/src/tint/transform/decompose_strided_matrix.h
index 40e9c3e..947dfc6 100644
--- a/src/tint/transform/decompose_strided_matrix.h
+++ b/src/tint/transform/decompose_strided_matrix.h
@@ -35,19 +35,10 @@
     /// Destructor
     ~DecomposeStridedMatrix() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/disable_uniformity_analysis.cc b/src/tint/transform/disable_uniformity_analysis.cc
index 918b1f1..ffd0b18 100644
--- a/src/tint/transform/disable_uniformity_analysis.cc
+++ b/src/tint/transform/disable_uniformity_analysis.cc
@@ -27,14 +27,20 @@
 
 DisableUniformityAnalysis::~DisableUniformityAnalysis() = default;
 
-bool DisableUniformityAnalysis::ShouldRun(const Program* program, const DataMap&) const {
-    return !program->Sem().Module()->Extensions().Contains(
-        ast::Extension::kChromiumDisableUniformityAnalysis);
-}
+Transform::ApplyResult DisableUniformityAnalysis::Apply(const Program* src,
+                                                        const DataMap&,
+                                                        DataMap&) const {
+    if (src->Sem().Module()->Extensions().Contains(
+            ast::Extension::kChromiumDisableUniformityAnalysis)) {
+        return SkipTransform;
+    }
 
-void DisableUniformityAnalysis::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    ctx.dst->Enable(ast::Extension::kChromiumDisableUniformityAnalysis);
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+    b.Enable(ast::Extension::kChromiumDisableUniformityAnalysis);
+
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/disable_uniformity_analysis.h b/src/tint/transform/disable_uniformity_analysis.h
index 3c9fb53..a9922af 100644
--- a/src/tint/transform/disable_uniformity_analysis.h
+++ b/src/tint/transform/disable_uniformity_analysis.h
@@ -27,19 +27,10 @@
     /// Destructor
     ~DisableUniformityAnalysis() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/expand_compound_assignment.cc b/src/tint/transform/expand_compound_assignment.cc
index 85b9bf6..9fa81dd 100644
--- a/src/tint/transform/expand_compound_assignment.cc
+++ b/src/tint/transform/expand_compound_assignment.cc
@@ -31,11 +31,9 @@
 
 namespace tint::transform {
 
-ExpandCompoundAssignment::ExpandCompoundAssignment() = default;
+namespace {
 
-ExpandCompoundAssignment::~ExpandCompoundAssignment() = default;
-
-bool ExpandCompoundAssignment::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     for (auto* node : program->ASTNodes().Objects()) {
         if (node->IsAnyOf<ast::CompoundAssignmentStatement, ast::IncrementDecrementStatement>()) {
             return true;
@@ -44,21 +42,10 @@
     return false;
 }
 
-namespace {
+}  // namespace
 
-/// Internal class used to collect statement expansions during the transform.
-class State {
-  private:
-    /// The clone context.
-    CloneContext& ctx;
-
-    /// The program builder.
-    ProgramBuilder& b;
-
-    /// The HoistToDeclBefore helper instance.
-    HoistToDeclBefore hoist_to_decl_before;
-
-  public:
+/// PIMPL state for the transform
+struct ExpandCompoundAssignment::State {
     /// Constructor
     /// @param context the clone context
     explicit State(CloneContext& context) : ctx(context), b(*ctx.dst), hoist_to_decl_before(ctx) {}
@@ -158,18 +145,32 @@
         ctx.Replace(stmt, b.Assign(new_lhs(), value));
     }
 
-    /// Finalize the transformation and clone the module.
-    void Finalize() {
-        hoist_to_decl_before.Apply();
-        ctx.Clone();
-    }
+  private:
+    /// The clone context.
+    CloneContext& ctx;
+
+    /// The program builder.
+    ProgramBuilder& b;
+
+    /// The HoistToDeclBefore helper instance.
+    HoistToDeclBefore hoist_to_decl_before;
 };
 
-}  // namespace
+ExpandCompoundAssignment::ExpandCompoundAssignment() = default;
 
-void ExpandCompoundAssignment::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+ExpandCompoundAssignment::~ExpandCompoundAssignment() = default;
+
+Transform::ApplyResult ExpandCompoundAssignment::Apply(const Program* src,
+                                                       const DataMap&,
+                                                       DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
     State state(ctx);
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
+    for (auto* node : src->ASTNodes().Objects()) {
         if (auto* assign = node->As<ast::CompoundAssignmentStatement>()) {
             state.Expand(assign, assign->lhs, ctx.Clone(assign->rhs), assign->op);
         } else if (auto* inc_dec = node->As<ast::IncrementDecrementStatement>()) {
@@ -178,7 +179,9 @@
             state.Expand(inc_dec, inc_dec->lhs, ctx.dst->Expr(1_a), op);
         }
     }
-    state.Finalize();
+
+    ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/expand_compound_assignment.h b/src/tint/transform/expand_compound_assignment.h
index 1081df7..6b299c5 100644
--- a/src/tint/transform/expand_compound_assignment.h
+++ b/src/tint/transform/expand_compound_assignment.h
@@ -45,19 +45,13 @@
     /// Destructor
     ~ExpandCompoundAssignment() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+  private:
+    struct State;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/first_index_offset.cc b/src/tint/transform/first_index_offset.cc
index cafca32..eb698be 100644
--- a/src/tint/transform/first_index_offset.cc
+++ b/src/tint/transform/first_index_offset.cc
@@ -35,6 +35,15 @@
 constexpr char kFirstVertexName[] = "first_vertex_index";
 constexpr char kFirstInstanceName[] = "first_instance_index";
 
+bool ShouldRun(const Program* program) {
+    for (auto* fn : program->AST().Functions()) {
+        if (fn->PipelineStage() == ast::PipelineStage::kVertex) {
+            return true;
+        }
+    }
+    return false;
+}
+
 }  // namespace
 
 FirstIndexOffset::BindingPoint::BindingPoint() = default;
@@ -49,16 +58,16 @@
 FirstIndexOffset::FirstIndexOffset() = default;
 FirstIndexOffset::~FirstIndexOffset() = default;
 
-bool FirstIndexOffset::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* fn : program->AST().Functions()) {
-        if (fn->PipelineStage() == ast::PipelineStage::kVertex) {
-            return true;
-        }
+Transform::ApplyResult FirstIndexOffset::Apply(const Program* src,
+                                               const DataMap& inputs,
+                                               DataMap& outputs) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
     }
-    return false;
-}
 
-void FirstIndexOffset::Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     // Get the uniform buffer binding point
     uint32_t ub_binding = binding_;
     uint32_t ub_group = group_;
@@ -115,17 +124,17 @@
     if (has_vertex_or_instance_index) {
         // Add uniform buffer members and calculate byte offsets
         utils::Vector<const ast::StructMember*, 8> members;
-        members.Push(ctx.dst->Member(kFirstVertexName, ctx.dst->ty.u32()));
-        members.Push(ctx.dst->Member(kFirstInstanceName, ctx.dst->ty.u32()));
-        auto* struct_ = ctx.dst->Structure(ctx.dst->Sym(), std::move(members));
+        members.Push(b.Member(kFirstVertexName, b.ty.u32()));
+        members.Push(b.Member(kFirstInstanceName, b.ty.u32()));
+        auto* struct_ = b.Structure(b.Sym(), std::move(members));
 
         // Create a global to hold the uniform buffer
-        Symbol buffer_name = ctx.dst->Sym();
-        ctx.dst->GlobalVar(buffer_name, ctx.dst->ty.Of(struct_), ast::AddressSpace::kUniform,
-                           utils::Vector{
-                               ctx.dst->Binding(AInt(ub_binding)),
-                               ctx.dst->Group(AInt(ub_group)),
-                           });
+        Symbol buffer_name = b.Sym();
+        b.GlobalVar(buffer_name, b.ty.Of(struct_), ast::AddressSpace::kUniform,
+                    utils::Vector{
+                        b.Binding(AInt(ub_binding)),
+                        b.Group(AInt(ub_group)),
+                    });
 
         // Fix up all references to the builtins with the offsets
         ctx.ReplaceAll([=, &ctx](const ast::Expression* expr) -> const ast::Expression* {
@@ -150,9 +159,10 @@
         });
     }
 
-    ctx.Clone();
-
     outputs.Add<Data>(has_vertex_or_instance_index);
+
+    ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/first_index_offset.h b/src/tint/transform/first_index_offset.h
index 04758cd..f84d811 100644
--- a/src/tint/transform/first_index_offset.h
+++ b/src/tint/transform/first_index_offset.h
@@ -103,19 +103,10 @@
     /// Destructor
     ~FirstIndexOffset() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
     uint32_t binding_ = 0;
diff --git a/src/tint/transform/for_loop_to_loop.cc b/src/tint/transform/for_loop_to_loop.cc
index e585790..63ccb12 100644
--- a/src/tint/transform/for_loop_to_loop.cc
+++ b/src/tint/transform/for_loop_to_loop.cc
@@ -14,17 +14,17 @@
 
 #include "src/tint/transform/for_loop_to_loop.h"
 
+#include <utility>
+
 #include "src/tint/ast/break_statement.h"
 #include "src/tint/program_builder.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::transform::ForLoopToLoop);
 
 namespace tint::transform {
-ForLoopToLoop::ForLoopToLoop() = default;
+namespace {
 
-ForLoopToLoop::~ForLoopToLoop() = default;
-
-bool ForLoopToLoop::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     for (auto* node : program->ASTNodes().Objects()) {
         if (node->Is<ast::ForLoopStatement>()) {
             return true;
@@ -33,19 +33,31 @@
     return false;
 }
 
-void ForLoopToLoop::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+}  // namespace
+
+ForLoopToLoop::ForLoopToLoop() = default;
+
+ForLoopToLoop::~ForLoopToLoop() = default;
+
+Transform::ApplyResult ForLoopToLoop::Apply(const Program* src, const DataMap&, DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     ctx.ReplaceAll([&](const ast::ForLoopStatement* for_loop) -> const ast::Statement* {
         utils::Vector<const ast::Statement*, 8> stmts;
         if (auto* cond = for_loop->condition) {
             // !condition
-            auto* not_cond =
-                ctx.dst->create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, ctx.Clone(cond));
+            auto* not_cond = b.Not(ctx.Clone(cond));
 
             // { break; }
-            auto* break_body = ctx.dst->Block(ctx.dst->create<ast::BreakStatement>());
+            auto* break_body = b.Block(b.Break());
 
             // if (!condition) { break; }
-            stmts.Push(ctx.dst->If(not_cond, break_body));
+            stmts.Push(b.If(not_cond, break_body));
         }
         for (auto* stmt : for_loop->body->statements) {
             stmts.Push(ctx.Clone(stmt));
@@ -53,20 +65,21 @@
 
         const ast::BlockStatement* continuing = nullptr;
         if (auto* cont = for_loop->continuing) {
-            continuing = ctx.dst->Block(ctx.Clone(cont));
+            continuing = b.Block(ctx.Clone(cont));
         }
 
-        auto* body = ctx.dst->Block(stmts);
-        auto* loop = ctx.dst->create<ast::LoopStatement>(body, continuing);
+        auto* body = b.Block(stmts);
+        auto* loop = b.Loop(body, continuing);
 
         if (auto* init = for_loop->initializer) {
-            return ctx.dst->Block(ctx.Clone(init), loop);
+            return b.Block(ctx.Clone(init), loop);
         }
 
         return loop;
     });
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/for_loop_to_loop.h b/src/tint/transform/for_loop_to_loop.h
index 5ab690a..fe3db97 100644
--- a/src/tint/transform/for_loop_to_loop.h
+++ b/src/tint/transform/for_loop_to_loop.h
@@ -29,19 +29,10 @@
     /// Destructor
     ~ForLoopToLoop() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/localize_struct_array_assignment.cc b/src/tint/transform/localize_struct_array_assignment.cc
index 8077393..bfe8865 100644
--- a/src/tint/transform/localize_struct_array_assignment.cc
+++ b/src/tint/transform/localize_struct_array_assignment.cc
@@ -32,70 +32,15 @@
 
 namespace tint::transform {
 
-/// Private implementation of LocalizeStructArrayAssignment transform
-class LocalizeStructArrayAssignment::State {
-  private:
-    CloneContext& ctx;
-    ProgramBuilder& b;
-
-    /// 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;
-        ast::TraverseExpressions(
-            expr, b.Diagnostics(), [&](const ast::IndexAccessorExpression* ia) {
-                // Indexing using a runtime value?
-                auto* idx_sem = ctx.src->Sem().Get(ia->index);
-                if (!idx_sem->ConstantValue()) {
-                    // Indexing a member access expr?
-                    if (auto* ma = ia->object->As<ast::MemberAccessorExpression>()) {
-                        // That accesses an array?
-                        if (ctx.src->TypeOf(ma)->UnwrapRef()->Is<sem::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 sem::Type*, ast::AddressSpace> GetOriginatingTypeAndAddressSpace(
-        const ast::AssignmentStatement* assign_stmt) {
-        auto* source_var = ctx.src->Sem().Get(assign_stmt->lhs)->SourceVariable();
-        if (!source_var) {
-            TINT_ICE(Transform, b.Diagnostics())
-                << "Unable to determine originating variable for lhs of assignment "
-                   "statement";
-            return {};
-        }
-
-        auto* type = source_var->Type();
-        if (auto* ref = type->As<sem::Reference>()) {
-            return {ref->StoreType(), ref->AddressSpace()};
-        } else if (auto* ptr = type->As<sem::Pointer>()) {
-            return {ptr->StoreType(), ptr->AddressSpace()};
-        }
-
-        TINT_ICE(Transform, b.Diagnostics())
-            << "Expecting to find variable of type pointer or reference on lhs "
-               "of assignment statement";
-        return {};
-    }
-
-  public:
+/// PIMPL state for the transform
+struct LocalizeStructArrayAssignment::State {
     /// Constructor
-    /// @param ctx_in the CloneContext primed with the input program and
-    /// ProgramBuilder
-    explicit State(CloneContext& ctx_in) : ctx(ctx_in), b(*ctx_in.dst) {}
+    /// @param program the source program
+    explicit State(const Program* program) : src(program) {}
 
     /// Runs the transform
-    void Run() {
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
         struct Shared {
             bool process_nested_nodes = false;
             utils::Vector<const ast::Statement*, 4> insert_before_stmts;
@@ -189,6 +134,65 @@
             });
 
         ctx.Clone();
+        return Program(std::move(b));
+    }
+
+  private:
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
+    /// The clone context
+    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;
+        ast::TraverseExpressions(
+            expr, b.Diagnostics(), [&](const ast::IndexAccessorExpression* ia) {
+                // Indexing using a runtime value?
+                auto* idx_sem = src->Sem().Get(ia->index);
+                if (!idx_sem->ConstantValue()) {
+                    // Indexing a member access expr?
+                    if (auto* ma = ia->object->As<ast::MemberAccessorExpression>()) {
+                        // That accesses an array?
+                        if (src->TypeOf(ma)->UnwrapRef()->Is<sem::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 sem::Type*, ast::AddressSpace> GetOriginatingTypeAndAddressSpace(
+        const ast::AssignmentStatement* assign_stmt) {
+        auto* source_var = src->Sem().Get(assign_stmt->lhs)->SourceVariable();
+        if (!source_var) {
+            TINT_ICE(Transform, b.Diagnostics())
+                << "Unable to determine originating variable for lhs of assignment "
+                   "statement";
+            return {};
+        }
+
+        auto* type = source_var->Type();
+        if (auto* ref = type->As<sem::Reference>()) {
+            return {ref->StoreType(), ref->AddressSpace()};
+        } else if (auto* ptr = type->As<sem::Pointer>()) {
+            return {ptr->StoreType(), ptr->AddressSpace()};
+        }
+
+        TINT_ICE(Transform, b.Diagnostics())
+            << "Expecting to find variable of type pointer or reference on lhs "
+               "of assignment statement";
+        return {};
     }
 };
 
@@ -196,9 +200,10 @@
 
 LocalizeStructArrayAssignment::~LocalizeStructArrayAssignment() = default;
 
-void LocalizeStructArrayAssignment::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    State state(ctx);
-    state.Run();
+Transform::ApplyResult LocalizeStructArrayAssignment::Apply(const Program* src,
+                                                            const DataMap&,
+                                                            DataMap&) const {
+    return State{src}.Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/localize_struct_array_assignment.h b/src/tint/transform/localize_struct_array_assignment.h
index 130f8cc..169e33c 100644
--- a/src/tint/transform/localize_struct_array_assignment.h
+++ b/src/tint/transform/localize_struct_array_assignment.h
@@ -36,17 +36,13 @@
     /// Destructor
     ~LocalizeStructArrayAssignment() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
-    class State;
+    struct State;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/manager.cc b/src/tint/transform/manager.cc
index 4e83320..79603c8 100644
--- a/src/tint/transform/manager.cc
+++ b/src/tint/transform/manager.cc
@@ -31,9 +31,9 @@
 Manager::Manager() = default;
 Manager::~Manager() = default;
 
-Output Manager::Run(const Program* program, const DataMap& data) const {
-    const Program* in = program;
-
+Transform::ApplyResult Manager::Apply(const Program* program,
+                                      const DataMap& inputs,
+                                      DataMap& outputs) const {
 #if TINT_PRINT_PROGRAM_FOR_EACH_TRANSFORM
     auto print_program = [&](const char* msg, const Transform* transform) {
         auto wgsl = Program::printer(in);
@@ -46,34 +46,30 @@
     };
 #endif
 
-    Output out;
+    std::optional<Program> output;
+
     for (const auto& transform : transforms_) {
-        if (!transform->ShouldRun(in, data)) {
-            TINT_IF_PRINT_PROGRAM(std::cout << "Skipping " << transform->TypeInfo().name
-                                            << std::endl);
-            continue;
-        }
         TINT_IF_PRINT_PROGRAM(print_program("Input to", transform.get()));
 
-        auto res = transform->Run(in, data);
-        out.program = std::move(res.program);
-        out.data.Add(std::move(res.data));
-        in = &out.program;
-        if (!in->IsValid()) {
-            TINT_IF_PRINT_PROGRAM(print_program("Invalid output of", transform.get()));
-            return out;
-        }
+        if (auto result = transform->Apply(program, inputs, outputs)) {
+            output.emplace(std::move(result.value()));
+            program = &output.value();
 
-        if (transform == transforms_.back()) {
-            TINT_IF_PRINT_PROGRAM(print_program("Output of", transform.get()));
+            if (!program->IsValid()) {
+                TINT_IF_PRINT_PROGRAM(print_program("Invalid output of", transform.get()));
+                break;
+            }
+
+            if (transform == transforms_.back()) {
+                TINT_IF_PRINT_PROGRAM(print_program("Output of", transform.get()));
+            }
+        } else {
+            TINT_IF_PRINT_PROGRAM(std::cout << "Skipped " << transform->TypeInfo().name
+                                            << std::endl);
         }
     }
 
-    if (program == in) {
-        out.program = program->Clone();
-    }
-
-    return out;
+    return output;
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/manager.h b/src/tint/transform/manager.h
index 9d4049f..64ca847 100644
--- a/src/tint/transform/manager.h
+++ b/src/tint/transform/manager.h
@@ -47,11 +47,10 @@
         transforms_.emplace_back(std::make_unique<T>(std::forward<ARGS>(args)...));
     }
 
-    /// Runs the transforms on `program`, returning the transformation result.
-    /// @param program the source program to transform
-    /// @param data optional extra transform-specific input data
-    /// @returns the transformed program and diagnostics
-    Output Run(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
     std::vector<std::unique_ptr<Transform>> transforms_;
diff --git a/src/tint/transform/merge_return.cc b/src/tint/transform/merge_return.cc
index aec6b6d..2b45b73 100644
--- a/src/tint/transform/merge_return.cc
+++ b/src/tint/transform/merge_return.cc
@@ -65,15 +65,6 @@
 
 MergeReturn::~MergeReturn() = default;
 
-bool MergeReturn::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* func : program->AST().Functions()) {
-        if (NeedsTransform(program, func)) {
-            return true;
-        }
-    }
-    return false;
-}
-
 namespace {
 
 /// Internal class used to during the transform.
@@ -223,7 +214,12 @@
 
 }  // namespace
 
-void MergeReturn::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+Transform::ApplyResult MergeReturn::Apply(const Program* src, const DataMap&, DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
+    bool made_changes = false;
+
     for (auto* func : ctx.src->AST().Functions()) {
         if (!NeedsTransform(ctx.src, func)) {
             continue;
@@ -231,9 +227,15 @@
 
         State state(ctx, func);
         state.ProcessStatement(func->body);
+        made_changes = true;
+    }
+
+    if (!made_changes) {
+        return SkipTransform;
     }
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/merge_return.h b/src/tint/transform/merge_return.h
index 1334a5c..f6db5c2 100644
--- a/src/tint/transform/merge_return.h
+++ b/src/tint/transform/merge_return.h
@@ -27,19 +27,10 @@
     /// Destructor
     ~MergeReturn() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.cc b/src/tint/transform/module_scope_var_to_entry_point_param.cc
index f9c11e5..16a622e 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.cc
@@ -38,6 +38,15 @@
 // The name of the struct member for arrays that are wrapped in structures.
 const char* kWrappedArrayMemberName = "arr";
 
+bool ShouldRun(const Program* program) {
+    for (auto* decl : program->AST().GlobalDeclarations()) {
+        if (decl->Is<ast::Variable>()) {
+            return true;
+        }
+    }
+    return false;
+}
+
 // Returns `true` if `type` is or contains a matrix type.
 bool ContainsMatrix(const sem::Type* type) {
     type = type->UnwrapRef();
@@ -56,7 +65,7 @@
 }
 }  // namespace
 
-/// State holds the current transform state.
+/// PIMPL state for the transform
 struct ModuleScopeVarToEntryPointParam::State {
     /// The clone context.
     CloneContext& ctx;
@@ -501,19 +510,20 @@
 
 ModuleScopeVarToEntryPointParam::~ModuleScopeVarToEntryPointParam() = default;
 
-bool ModuleScopeVarToEntryPointParam::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* decl : program->AST().GlobalDeclarations()) {
-        if (decl->Is<ast::Variable>()) {
-            return true;
-        }
+Transform::ApplyResult ModuleScopeVarToEntryPointParam::Apply(const Program* src,
+                                                              const DataMap&,
+                                                              DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
     }
-    return false;
-}
 
-void ModuleScopeVarToEntryPointParam::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
     State state{ctx};
     state.Process();
+
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.h b/src/tint/transform/module_scope_var_to_entry_point_param.h
index 75bdaf3..377151f 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.h
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.h
@@ -69,20 +69,12 @@
     /// Destructor
     ~ModuleScopeVarToEntryPointParam() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
-
+  private:
     struct State;
 };
 
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index 002b858..c3ebf4a 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -31,6 +31,17 @@
 namespace tint::transform {
 namespace {
 
+bool ShouldRun(const Program* program) {
+    for (auto* node : program->ASTNodes().Objects()) {
+        if (auto* ty = node->As<ast::Type>()) {
+            if (program->Sem().Get<sem::ExternalTexture>(ty)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 /// This struct stores symbols for new bindings created as a result of transforming a
 /// texture_external instance.
 struct NewBindingSymbols {
@@ -40,7 +51,7 @@
 };
 }  // namespace
 
-/// State holds the current transform state
+/// PIMPL state for the transform
 struct MultiplanarExternalTexture::State {
     /// The clone context.
     CloneContext& ctx;
@@ -537,30 +548,26 @@
 MultiplanarExternalTexture::MultiplanarExternalTexture() = default;
 MultiplanarExternalTexture::~MultiplanarExternalTexture() = default;
 
-bool MultiplanarExternalTexture::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* node : program->ASTNodes().Objects()) {
-        if (auto* ty = node->As<ast::Type>()) {
-            if (program->Sem().Get<sem::ExternalTexture>(ty)) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
 // Within this transform, an instance of a texture_external binding is unpacked into two
 // texture_2d<f32> bindings representing two possible planes of a single texture and a uniform
 // buffer binding representing a struct of parameters. Calls to texture builtins that contain a
 // texture_external parameter will be transformed into a newly generated version of the function,
 // which can perform the desired operation on a single RGBA plane or on separate Y and UV planes.
-void MultiplanarExternalTexture::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const {
+Transform::ApplyResult MultiplanarExternalTexture::Apply(const Program* src,
+                                                         const DataMap& inputs,
+                                                         DataMap&) const {
     auto* new_binding_points = inputs.Get<NewBindingPoints>();
 
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
     if (!new_binding_points) {
-        ctx.dst->Diagnostics().add_error(
-            diag::System::Transform,
-            "missing new binding point data for " + std::string(TypeInfo().name));
-        return;
+        b.Diagnostics().add_error(diag::System::Transform, "missing new binding point data for " +
+                                                               std::string(TypeInfo().name));
+        return Program(std::move(b));
     }
 
     State state(ctx, new_binding_points);
@@ -568,6 +575,7 @@
     state.Process();
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/multiplanar_external_texture.h b/src/tint/transform/multiplanar_external_texture.h
index a10fed4..695e38c 100644
--- a/src/tint/transform/multiplanar_external_texture.h
+++ b/src/tint/transform/multiplanar_external_texture.h
@@ -80,21 +80,13 @@
     /// Destructor
     ~MultiplanarExternalTexture() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-  protected:
+  private:
     struct State;
-
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/multiplanar_external_texture_test.cc b/src/tint/transform/multiplanar_external_texture_test.cc
index dacbb1e..4416d35 100644
--- a/src/tint/transform/multiplanar_external_texture_test.cc
+++ b/src/tint/transform/multiplanar_external_texture_test.cc
@@ -23,7 +23,11 @@
 TEST_F(MultiplanarExternalTextureTest, ShouldRunEmptyModule) {
     auto* src = R"()";
 
-    EXPECT_FALSE(ShouldRun<MultiplanarExternalTexture>(src));
+    DataMap data;
+    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+        MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
+
+    EXPECT_FALSE(ShouldRun<MultiplanarExternalTexture>(src, data));
 }
 
 TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureAlias) {
@@ -31,14 +35,22 @@
 type ET = texture_external;
 )";
 
-    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src));
+    DataMap data;
+    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+        MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
+
+    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
 }
 TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureGlobal) {
     auto* src = R"(
 @group(0) @binding(0) var ext_tex : texture_external;
 )";
 
-    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src));
+    DataMap data;
+    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+        MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
+
+    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
 }
 
 TEST_F(MultiplanarExternalTextureTest, ShouldRunHasExternalTextureParam) {
@@ -46,7 +58,11 @@
 fn f(ext_tex : texture_external) {}
 )";
 
-    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src));
+    DataMap data;
+    data.Add<MultiplanarExternalTexture::NewBindingPoints>(
+        MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
+
+    EXPECT_TRUE(ShouldRun<MultiplanarExternalTexture>(src, data));
 }
 
 // Running the transform without passing in data for the new bindings should result in an error.
diff --git a/src/tint/transform/num_workgroups_from_uniform.cc b/src/tint/transform/num_workgroups_from_uniform.cc
index 2122f07..e6681ca 100644
--- a/src/tint/transform/num_workgroups_from_uniform.cc
+++ b/src/tint/transform/num_workgroups_from_uniform.cc
@@ -29,6 +29,18 @@
 
 namespace tint::transform {
 namespace {
+
+bool ShouldRun(const Program* program) {
+    for (auto* node : program->ASTNodes().Objects()) {
+        if (auto* attr = node->As<ast::BuiltinAttribute>()) {
+            if (attr->builtin == ast::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 {
@@ -44,41 +56,40 @@
         size_t operator()(const Accessor& a) const { return utils::Hash(a.param, a.member); }
     };
 };
+
 }  // namespace
 
 NumWorkgroupsFromUniform::NumWorkgroupsFromUniform() = default;
 NumWorkgroupsFromUniform::~NumWorkgroupsFromUniform() = default;
 
-bool NumWorkgroupsFromUniform::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* node : program->ASTNodes().Objects()) {
-        if (auto* attr = node->As<ast::BuiltinAttribute>()) {
-            if (attr->builtin == ast::BuiltinValue::kNumWorkgroups) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
+Transform::ApplyResult NumWorkgroupsFromUniform::Apply(const Program* src,
+                                                       const DataMap& inputs,
+                                                       DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
 
-void NumWorkgroupsFromUniform::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const {
     auto* cfg = inputs.Get<Config>();
     if (cfg == nullptr) {
-        ctx.dst->Diagnostics().add_error(
-            diag::System::Transform, "missing transform data for " + std::string(TypeInfo().name));
-        return;
+        b.Diagnostics().add_error(diag::System::Transform,
+                                  "missing transform data for " + std::string(TypeInfo().name));
+        return Program(std::move(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 : ctx.src->AST().Functions()) {
+    for (auto* func : src->AST().Functions()) {
         // num_workgroups is only valid for compute stages.
         if (func->PipelineStage() != ast::PipelineStage::kCompute) {
             continue;
         }
 
-        for (auto* param : ctx.src->Sem().Get(func)->Parameters()) {
+        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>();
@@ -108,7 +119,7 @@
                 // If this is the only member, remove the struct and parameter too.
                 if (str->Members().size() == 1) {
                     ctx.Remove(func->params, param->Declaration());
-                    ctx.Remove(ctx.src->AST().GlobalDeclarations(), str->Declaration());
+                    ctx.Remove(src->AST().GlobalDeclarations(), str->Declaration());
                 }
             }
         }
@@ -119,11 +130,10 @@
     const ast::Variable* num_workgroups_ubo = nullptr;
     auto get_ubo = [&]() {
         if (!num_workgroups_ubo) {
-            auto* num_workgroups_struct = ctx.dst->Structure(
-                ctx.dst->Sym(),
-                utils::Vector{
-                    ctx.dst->Member(kNumWorkgroupsMemberName, ctx.dst->ty.vec3(ctx.dst->ty.u32())),
-                });
+            auto* num_workgroups_struct =
+                b.Structure(b.Sym(), utils::Vector{
+                                         b.Member(kNumWorkgroupsMemberName, b.ty.vec3(b.ty.u32())),
+                                     });
 
             uint32_t group, binding;
             if (cfg->ubo_binding.has_value()) {
@@ -135,9 +145,9 @@
                 // plus 1, or group 0 if no resource bound.
                 group = 0;
 
-                for (auto* global : ctx.src->AST().GlobalVariables()) {
+                for (auto* global : src->AST().GlobalVariables()) {
                     if (global->HasBindingPoint()) {
-                        auto* global_sem = ctx.src->Sem().Get<sem::GlobalVariable>(global);
+                        auto* global_sem = src->Sem().Get<sem::GlobalVariable>(global);
                         auto binding_point = global_sem->BindingPoint();
                         if (binding_point.group >= group) {
                             group = binding_point.group + 1;
@@ -148,16 +158,16 @@
                 binding = 0;
             }
 
-            num_workgroups_ubo = ctx.dst->GlobalVar(
-                ctx.dst->Sym(), ctx.dst->ty.Of(num_workgroups_struct), ast::AddressSpace::kUniform,
-                ctx.dst->Group(AInt(group)), ctx.dst->Binding(AInt(binding)));
+            num_workgroups_ubo =
+                b.GlobalVar(b.Sym(), b.ty.Of(num_workgroups_struct), ast::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 : ctx.src->ASTNodes().Objects()) {
+    for (auto* node : src->ASTNodes().Objects()) {
         auto* accessor = node->As<ast::MemberAccessorExpression>();
         if (!accessor) {
             continue;
@@ -168,12 +178,12 @@
         }
 
         if (to_replace.count({ident->symbol, accessor->member->symbol})) {
-            ctx.Replace(accessor,
-                        ctx.dst->MemberAccessor(get_ubo()->symbol, kNumWorkgroupsMemberName));
+            ctx.Replace(accessor, b.MemberAccessor(get_ubo()->symbol, kNumWorkgroupsMemberName));
         }
     }
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 NumWorkgroupsFromUniform::Config::Config(std::optional<sem::BindingPoint> ubo_bp)
diff --git a/src/tint/transform/num_workgroups_from_uniform.h b/src/tint/transform/num_workgroups_from_uniform.h
index 292c823..25308f2 100644
--- a/src/tint/transform/num_workgroups_from_uniform.h
+++ b/src/tint/transform/num_workgroups_from_uniform.h
@@ -72,19 +72,10 @@
         std::optional<sem::BindingPoint> ubo_binding;
     };
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/num_workgroups_from_uniform_test.cc b/src/tint/transform/num_workgroups_from_uniform_test.cc
index 093081c..435ab33 100644
--- a/src/tint/transform/num_workgroups_from_uniform_test.cc
+++ b/src/tint/transform/num_workgroups_from_uniform_test.cc
@@ -28,7 +28,9 @@
 TEST_F(NumWorkgroupsFromUniformTest, ShouldRunEmptyModule) {
     auto* src = R"()";
 
-    EXPECT_FALSE(ShouldRun<NumWorkgroupsFromUniform>(src));
+    DataMap data;
+    data.Add<NumWorkgroupsFromUniform::Config>(sem::BindingPoint{0, 30u});
+    EXPECT_FALSE(ShouldRun<NumWorkgroupsFromUniform>(src, data));
 }
 
 TEST_F(NumWorkgroupsFromUniformTest, ShouldRunHasNumWorkgroups) {
@@ -38,7 +40,9 @@
 }
 )";
 
-    EXPECT_TRUE(ShouldRun<NumWorkgroupsFromUniform>(src));
+    DataMap data;
+    data.Add<NumWorkgroupsFromUniform::Config>(sem::BindingPoint{0, 30u});
+    EXPECT_TRUE(ShouldRun<NumWorkgroupsFromUniform>(src, data));
 }
 
 TEST_F(NumWorkgroupsFromUniformTest, Error_MissingTransformData) {
@@ -55,7 +59,6 @@
     DataMap data;
     data.Add<CanonicalizeEntryPointIO::Config>(CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
     auto got = Run<Unshadow, CanonicalizeEntryPointIO, NumWorkgroupsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
 }
 
diff --git a/src/tint/transform/packed_vec3.cc b/src/tint/transform/packed_vec3.cc
index dde5aca..e947a53 100644
--- a/src/tint/transform/packed_vec3.cc
+++ b/src/tint/transform/packed_vec3.cc
@@ -33,14 +33,15 @@
 
 namespace tint::transform {
 
-/// The PIMPL state for the PackedVec3 transform
+/// PIMPL state for the transform
 struct PackedVec3::State {
     /// Constructor
-    /// @param c the CloneContext
-    explicit State(CloneContext& c) : ctx(c) {}
+    /// @param program the source program
+    explicit State(const Program* program) : src(program) {}
 
     /// Runs the transform
-    void Run() {
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
         // Packed vec3<T> struct members
         utils::Hashset<const sem::StructMember*, 8> members;
 
@@ -72,6 +73,10 @@
             }
         }
 
+        if (members.IsEmpty()) {
+            return SkipTransform;
+        }
+
         // Walk the nodes, starting with the most deeply nested, finding all the AST expressions
         // that load a whole packed vector (not a scalar / swizzle of the vector).
         utils::Hashset<const sem::Expression*, 16> refs;
@@ -137,36 +142,20 @@
         }
 
         ctx.Clone();
-    }
-
-    /// @returns true if this transform should be run for the given program
-    /// @param program the program to inspect
-    static bool ShouldRun(const Program* program) {
-        for (auto* decl : program->AST().GlobalDeclarations()) {
-            if (auto* str = program->Sem().Get<sem::Struct>(decl)) {
-                if (str->IsHostShareable()) {
-                    for (auto* member : str->Members()) {
-                        if (auto* vec = member->Type()->As<sem::Vector>()) {
-                            if (vec->Width() == 3) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
+        return Program(std::move(b));
     }
 
   private:
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
     /// The clone context
-    CloneContext& ctx;
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
     /// Alias to the semantic info in ctx.src
     const sem::Info& sem = ctx.src->Sem();
     /// Alias to the symbols in ctx.src
     const SymbolTable& sym = ctx.src->Symbols();
-    /// Alias to the ctx.dst program builder
-    ProgramBuilder& b = *ctx.dst;
 };
 
 PackedVec3::Attribute::Attribute(ProgramID pid, ast::NodeID nid) : Base(pid, nid) {}
@@ -183,12 +172,8 @@
 PackedVec3::PackedVec3() = default;
 PackedVec3::~PackedVec3() = default;
 
-bool PackedVec3::ShouldRun(const Program* program, const DataMap&) const {
-    return State::ShouldRun(program);
-}
-
-void PackedVec3::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    State(ctx).Run();
+Transform::ApplyResult PackedVec3::Apply(const Program* src, const DataMap&, DataMap&) const {
+    return State{src}.Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/packed_vec3.h b/src/tint/transform/packed_vec3.h
index 9d899cb..0d304fa 100644
--- a/src/tint/transform/packed_vec3.h
+++ b/src/tint/transform/packed_vec3.h
@@ -56,21 +56,13 @@
     /// Destructor
     ~PackedVec3() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
     struct State;
-
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/pad_structs.cc b/src/tint/transform/pad_structs.cc
index 10b0565..4ceb39d 100644
--- a/src/tint/transform/pad_structs.cc
+++ b/src/tint/transform/pad_structs.cc
@@ -50,8 +50,10 @@
 
 PadStructs::~PadStructs() = default;
 
-void PadStructs::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    auto& sem = ctx.src->Sem();
+Transform::ApplyResult PadStructs::Apply(const Program* src, const DataMap&, DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+    auto& sem = src->Sem();
 
     std::unordered_map<const ast::Struct*, const ast::Struct*> replaced_structs;
     utils::Hashset<const ast::StructMember*, 8> padding_members;
@@ -65,7 +67,7 @@
         bool has_runtime_sized_array = false;
         utils::Vector<const ast::StructMember*, 8> new_members;
         for (auto* mem : str->Members()) {
-            auto name = ctx.src->Symbols().NameFor(mem->Name());
+            auto name = src->Symbols().NameFor(mem->Name());
 
             if (offset < mem->Offset()) {
                 CreatePadding(&new_members, &padding_members, ctx.dst, mem->Offset() - offset);
@@ -75,7 +77,7 @@
             auto* ty = mem->Type();
             const ast::Type* type = CreateASTTypeFor(ctx, ty);
 
-            new_members.Push(ctx.dst->Member(name, type));
+            new_members.Push(b.Member(name, type));
 
             uint32_t size = ty->Size();
             if (ty->Is<sem::Struct>() && str->UsedAs(ast::AddressSpace::kUniform)) {
@@ -97,8 +99,8 @@
         if (offset < struct_size && !has_runtime_sized_array) {
             CreatePadding(&new_members, &padding_members, ctx.dst, struct_size - offset);
         }
-        auto* new_struct = ctx.dst->create<ast::Struct>(ctx.Clone(ast_str->name),
-                                                        std::move(new_members), utils::Empty);
+        auto* new_struct =
+            b.create<ast::Struct>(ctx.Clone(ast_str->name), std::move(new_members), utils::Empty);
         replaced_structs[ast_str] = new_struct;
         return new_struct;
     });
@@ -131,16 +133,17 @@
         auto* arg = ast_call->args.begin();
         for (auto* member : new_struct->members) {
             if (padding_members.Contains(member)) {
-                new_args.Push(ctx.dst->Expr(0_u));
+                new_args.Push(b.Expr(0_u));
             } else {
                 new_args.Push(ctx.Clone(*arg));
                 arg++;
             }
         }
-        return ctx.dst->Construct(CreateASTTypeFor(ctx, str), new_args);
+        return b.Construct(CreateASTTypeFor(ctx, str), new_args);
     });
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/pad_structs.h b/src/tint/transform/pad_structs.h
index 55fec74..e9996d4 100644
--- a/src/tint/transform/pad_structs.h
+++ b/src/tint/transform/pad_structs.h
@@ -30,14 +30,10 @@
     /// Destructor
     ~PadStructs() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/promote_initializers_to_let.cc b/src/tint/transform/promote_initializers_to_let.cc
index 22cdc20..9e02c45 100644
--- a/src/tint/transform/promote_initializers_to_let.cc
+++ b/src/tint/transform/promote_initializers_to_let.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "src/tint/transform/promote_initializers_to_let.h"
+
+#include <utility>
+
 #include "src/tint/program_builder.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/statement.h"
@@ -27,9 +30,16 @@
 
 PromoteInitializersToLet::~PromoteInitializersToLet() = default;
 
-void PromoteInitializersToLet::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+Transform::ApplyResult PromoteInitializersToLet::Apply(const Program* src,
+                                                       const DataMap&,
+                                                       DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     HoistToDeclBefore hoist_to_decl_before(ctx);
 
+    bool any_promoted = false;
+
     // Hoists array and structure initializers to a constant variable, declared
     // just before the statement of usage.
     auto promote = [&](const sem::Expression* expr) {
@@ -59,14 +69,15 @@
             return true;
         }
 
+        any_promoted = true;
         return hoist_to_decl_before.Add(expr, expr->Declaration(), true);
     };
 
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
+    for (auto* node : src->ASTNodes().Objects()) {
         bool ok = Switch(
             node,  //
             [&](const ast::CallExpression* expr) {
-                if (auto* sem = ctx.src->Sem().Get(expr)) {
+                if (auto* sem = src->Sem().Get(expr)) {
                     auto* ctor = sem->UnwrapMaterialize()->As<sem::Call>();
                     if (ctor->Target()->Is<sem::TypeInitializer>()) {
                         return promote(sem);
@@ -75,7 +86,7 @@
                 return true;
             },
             [&](const ast::IdentifierExpression* expr) {
-                if (auto* sem = ctx.src->Sem().Get(expr)) {
+                if (auto* sem = src->Sem().Get(expr)) {
                     if (auto* user = sem->UnwrapMaterialize()->As<sem::VariableUser>()) {
                         // Identifier resolves to a variable
                         if (auto* stmt = user->Stmt()) {
@@ -96,14 +107,17 @@
                 return true;
             },
             [&](Default) { return true; });
-
         if (!ok) {
-            return;
+            return Program(std::move(b));
         }
     }
 
-    hoist_to_decl_before.Apply();
+    if (!any_promoted) {
+        return SkipTransform;
+    }
+
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/promote_initializers_to_let.h b/src/tint/transform/promote_initializers_to_let.h
index 78793c7..b1bb291 100644
--- a/src/tint/transform/promote_initializers_to_let.h
+++ b/src/tint/transform/promote_initializers_to_let.h
@@ -33,14 +33,10 @@
     /// Destructor
     ~PromoteInitializersToLet() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/promote_side_effects_to_decl.cc b/src/tint/transform/promote_side_effects_to_decl.cc
index e36c416..ea13b0b 100644
--- a/src/tint/transform/promote_side_effects_to_decl.cc
+++ b/src/tint/transform/promote_side_effects_to_decl.cc
@@ -53,36 +53,36 @@
 // to else {if}s so that the next transform, DecomposeSideEffects, can insert
 // hoisted expressions above their current location.
 struct SimplifySideEffectStatements : Castable<PromoteSideEffectsToDecl, Transform> {
-    class State;
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const override;
+    ApplyResult Apply(const Program* src, const DataMap& inputs, DataMap& outputs) const override;
 };
 
-class SimplifySideEffectStatements::State : public StateBase {
-    HoistToDeclBefore hoist_to_decl_before;
+Transform::ApplyResult SimplifySideEffectStatements::Apply(const Program* src,
+                                                           const DataMap&,
+                                                           DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
 
-  public:
-    explicit State(CloneContext& ctx_in) : StateBase(ctx_in), hoist_to_decl_before(ctx_in) {}
+    bool made_changes = false;
 
-    void Run() {
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
-            if (auto* expr = node->As<ast::Expression>()) {
-                auto* sem_expr = sem.Get(expr);
-                if (!sem_expr || !sem_expr->HasSideEffects()) {
-                    continue;
-                }
-
-                hoist_to_decl_before.Prepare(sem_expr);
+    HoistToDeclBefore hoist_to_decl_before(ctx);
+    for (auto* node : ctx.src->ASTNodes().Objects()) {
+        if (auto* expr = node->As<ast::Expression>()) {
+            auto* sem_expr = src->Sem().Get(expr);
+            if (!sem_expr || !sem_expr->HasSideEffects()) {
+                continue;
             }
+
+            hoist_to_decl_before.Prepare(sem_expr);
+            made_changes = true;
         }
-
-        hoist_to_decl_before.Apply();
-        ctx.Clone();
     }
-};
 
-void SimplifySideEffectStatements::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    State state(ctx);
-    state.Run();
+    if (!made_changes) {
+        return SkipTransform;
+    }
+
+    ctx.Clone();
+    return Program(std::move(b));
 }
 
 // Decomposes side-effecting expressions to ensure order of evaluation. This
@@ -91,7 +91,7 @@
 struct DecomposeSideEffects : Castable<PromoteSideEffectsToDecl, Transform> {
     class CollectHoistsState;
     class DecomposeState;
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const override;
+    ApplyResult Apply(const Program* src, const DataMap& inputs, DataMap& outputs) const override;
 };
 
 // CollectHoistsState traverses the AST top-down, identifying which expressions
@@ -669,12 +669,15 @@
             }
             return nullptr;
         });
-
-        ctx.Clone();
     }
 };
 
-void DecomposeSideEffects::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+Transform::ApplyResult DecomposeSideEffects::Apply(const Program* src,
+                                                   const DataMap&,
+                                                   DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     // First collect side-effecting expressions to hoist
     CollectHoistsState collect_hoists_state{ctx};
     auto to_hoist = collect_hoists_state.Run();
@@ -682,6 +685,9 @@
     // Now decompose these expressions
     DecomposeState decompose_state{ctx, std::move(to_hoist)};
     decompose_state.Run();
+
+    ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace
@@ -689,13 +695,13 @@
 PromoteSideEffectsToDecl::PromoteSideEffectsToDecl() = default;
 PromoteSideEffectsToDecl::~PromoteSideEffectsToDecl() = default;
 
-Output PromoteSideEffectsToDecl::Run(const Program* program, const DataMap& data) const {
+Transform::ApplyResult PromoteSideEffectsToDecl::Apply(const Program* src,
+                                                       const DataMap& inputs,
+                                                       DataMap& outputs) const {
     transform::Manager manager;
     manager.Add<SimplifySideEffectStatements>();
     manager.Add<DecomposeSideEffects>();
-
-    auto output = manager.Run(program, data);
-    return output;
+    return manager.Apply(src, inputs, outputs);
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/promote_side_effects_to_decl.h b/src/tint/transform/promote_side_effects_to_decl.h
index d5d1126..99e80c6 100644
--- a/src/tint/transform/promote_side_effects_to_decl.h
+++ b/src/tint/transform/promote_side_effects_to_decl.h
@@ -31,12 +31,10 @@
     /// Destructor
     ~PromoteSideEffectsToDecl() override;
 
-  protected:
-    /// Runs the transform on `program`, returning the transformation result.
-    /// @param program the source program to transform
-    /// @param data optional extra transform-specific data
-    /// @returns the transformation result
-    Output Run(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/remove_continue_in_switch.cc b/src/tint/transform/remove_continue_in_switch.cc
index e5df23f..cf0158f 100644
--- a/src/tint/transform/remove_continue_in_switch.cc
+++ b/src/tint/transform/remove_continue_in_switch.cc
@@ -32,53 +32,19 @@
 TINT_INSTANTIATE_TYPEINFO(tint::transform::RemoveContinueInSwitch);
 
 namespace tint::transform {
-namespace {
 
-class State {
-  private:
-    CloneContext& ctx;
-    ProgramBuilder& b;
-    const sem::Info& sem;
-
-    // Map of switch statement to 'tint_continue' variable.
-    std::unordered_map<const ast::SwitchStatement*, Symbol> switch_to_cont_var_name;
-
-    // If `cont` is within a switch statement within a loop, returns a pointer to
-    // that switch statement.
-    static const ast::SwitchStatement* GetParentSwitchInLoop(const sem::Info& sem,
-                                                             const ast::ContinueStatement* cont) {
-        // Find whether first parent is a switch or a loop
-        auto* sem_stmt = sem.Get(cont);
-        auto* sem_parent = sem_stmt->FindFirstParent<sem::SwitchStatement, sem::LoopBlockStatement,
-                                                     sem::ForLoopStatement, sem::WhileStatement>();
-        if (!sem_parent) {
-            return nullptr;
-        }
-        return sem_parent->Declaration()->As<ast::SwitchStatement>();
-    }
-
-  public:
+/// PIMPL state for the transform
+struct RemoveContinueInSwitch::State {
     /// Constructor
-    /// @param ctx_in the context
-    explicit State(CloneContext& ctx_in) : ctx(ctx_in), b(*ctx_in.dst), sem(ctx_in.src->Sem()) {}
-
-    /// Returns true if this transform should be run for the given program
-    static bool ShouldRun(const Program* program) {
-        for (auto* node : program->ASTNodes().Objects()) {
-            auto* stmt = node->As<ast::ContinueStatement>();
-            if (!stmt) {
-                continue;
-            }
-            if (GetParentSwitchInLoop(program->Sem(), stmt)) {
-                return true;
-            }
-        }
-        return false;
-    }
+    /// @param program the source program
+    explicit State(const Program* program) : src(program) {}
 
     /// Runs the transform
-    void Run() {
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
+        bool made_changes = false;
+
+        for (auto* node : src->ASTNodes().Objects()) {
             auto* cont = node->As<ast::ContinueStatement>();
             if (!cont) {
                 continue;
@@ -90,6 +56,8 @@
                 continue;
             }
 
+            made_changes = true;
+
             auto cont_var_name =
                 tint::utils::GetOrCreate(switch_to_cont_var_name, switch_stmt, [&]() {
                     // Create and insert 'var tint_continue : bool = false;' before the
@@ -116,22 +84,50 @@
             ctx.Replace(cont, new_stmt);
         }
 
+        if (!made_changes) {
+            return SkipTransform;
+        }
+
         ctx.Clone();
+        return Program(std::move(b));
+    }
+
+  private:
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
+    /// The clone context
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
+    /// Alias to src->sem
+    const sem::Info& sem = src->Sem();
+
+    // Map of switch statement to 'tint_continue' variable.
+    std::unordered_map<const ast::SwitchStatement*, Symbol> switch_to_cont_var_name;
+
+    // If `cont` is within a switch statement within a loop, returns a pointer to
+    // that switch statement.
+    static const ast::SwitchStatement* GetParentSwitchInLoop(const sem::Info& sem,
+                                                             const ast::ContinueStatement* cont) {
+        // Find whether first parent is a switch or a loop
+        auto* sem_stmt = sem.Get(cont);
+        auto* sem_parent = sem_stmt->FindFirstParent<sem::SwitchStatement, sem::LoopBlockStatement,
+                                                     sem::ForLoopStatement, sem::WhileStatement>();
+        if (!sem_parent) {
+            return nullptr;
+        }
+        return sem_parent->Declaration()->As<ast::SwitchStatement>();
     }
 };
 
-}  // namespace
-
 RemoveContinueInSwitch::RemoveContinueInSwitch() = default;
 RemoveContinueInSwitch::~RemoveContinueInSwitch() = default;
 
-bool RemoveContinueInSwitch::ShouldRun(const Program* program, const DataMap& /*data*/) const {
-    return State::ShouldRun(program);
-}
-
-void RemoveContinueInSwitch::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    State state(ctx);
-    state.Run();
+Transform::ApplyResult RemoveContinueInSwitch::Apply(const Program* src,
+                                                     const DataMap&,
+                                                     DataMap&) const {
+    State state(src);
+    return state.Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/remove_continue_in_switch.h b/src/tint/transform/remove_continue_in_switch.h
index 9e5a4d5..1070906 100644
--- a/src/tint/transform/remove_continue_in_switch.h
+++ b/src/tint/transform/remove_continue_in_switch.h
@@ -31,19 +31,13 @@
     /// Destructor
     ~RemoveContinueInSwitch() override;
 
-  protected:
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+  private:
+    struct State;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/remove_phonies.cc b/src/tint/transform/remove_phonies.cc
index 1f84538..080152c 100644
--- a/src/tint/transform/remove_phonies.cc
+++ b/src/tint/transform/remove_phonies.cc
@@ -41,34 +41,25 @@
 
 RemovePhonies::~RemovePhonies() = default;
 
-bool RemovePhonies::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* node : program->ASTNodes().Objects()) {
-        if (node->Is<ast::PhonyExpression>()) {
-            return true;
-        }
-        if (auto* stmt = node->As<ast::CallStatement>()) {
-            if (program->Sem().Get(stmt->expr)->ConstantValue() != nullptr) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
+Transform::ApplyResult RemovePhonies::Apply(const Program* src, const DataMap&, DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
 
-void RemovePhonies::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    auto& sem = ctx.src->Sem();
+    auto& sem = src->Sem();
 
-    std::unordered_map<SinkSignature, Symbol, utils::Hasher<SinkSignature>> sinks;
+    utils::Hashmap<SinkSignature, Symbol, 8, utils::Hasher<SinkSignature>> sinks;
 
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
+    bool made_changes = false;
+    for (auto* node : src->ASTNodes().Objects()) {
         Switch(
             node,
             [&](const ast::AssignmentStatement* stmt) {
                 if (stmt->lhs->Is<ast::PhonyExpression>()) {
+                    made_changes = true;
+
                     std::vector<const ast::Expression*> side_effects;
                     if (!ast::TraverseExpressions(
-                            stmt->rhs, ctx.dst->Diagnostics(),
-                            [&](const ast::CallExpression* expr) {
+                            stmt->rhs, b.Diagnostics(), [&](const ast::CallExpression* expr) {
                                 // ast::CallExpression may map to a function or builtin call
                                 // (both may have side-effects), or a type initializer or
                                 // type conversion (both do not have side effects).
@@ -100,8 +91,7 @@
                         if (auto* call = side_effects[0]->As<ast::CallExpression>()) {
                             // Phony assignment with single call side effect.
                             // Replace phony assignment with call.
-                            ctx.Replace(stmt,
-                                        [&, call] { return ctx.dst->CallStmt(ctx.Clone(call)); });
+                            ctx.Replace(stmt, [&, call] { return b.CallStmt(ctx.Clone(call)); });
                             return;
                         }
                     }
@@ -114,22 +104,21 @@
                         for (auto* arg : side_effects) {
                             sig.push_back(sem.Get(arg)->Type()->UnwrapRef());
                         }
-                        auto sink = utils::GetOrCreate(sinks, sig, [&] {
-                            auto name = ctx.dst->Symbols().New("phony_sink");
+                        auto sink = sinks.GetOrCreate(sig, [&] {
+                            auto name = b.Symbols().New("phony_sink");
                             utils::Vector<const ast::Parameter*, 8> params;
                             for (auto* ty : sig) {
                                 auto* ast_ty = CreateASTTypeFor(ctx, ty);
-                                params.Push(
-                                    ctx.dst->Param("p" + std::to_string(params.Length()), ast_ty));
+                                params.Push(b.Param("p" + std::to_string(params.Length()), ast_ty));
                             }
-                            ctx.dst->Func(name, params, ctx.dst->ty.void_(), {});
+                            b.Func(name, params, b.ty.void_(), {});
                             return name;
                         });
                         utils::Vector<const ast::Expression*, 8> args;
                         for (auto* arg : side_effects) {
                             args.Push(ctx.Clone(arg));
                         }
-                        return ctx.dst->CallStmt(ctx.dst->Call(sink, args));
+                        return b.CallStmt(b.Call(sink, args));
                     });
                 }
             },
@@ -138,12 +127,18 @@
                 // TODO(crbug.com/tint/1637): Remove if `stmt->expr` has no side-effects.
                 auto* sem_expr = sem.Get(stmt->expr);
                 if ((sem_expr->ConstantValue() != nullptr) && !sem_expr->HasSideEffects()) {
+                    made_changes = true;
                     ctx.Remove(sem.Get(stmt)->Block()->Declaration()->statements, stmt);
                 }
             });
     }
 
+    if (!made_changes) {
+        return SkipTransform;
+    }
+
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/remove_phonies.h b/src/tint/transform/remove_phonies.h
index daa1812..99a049e 100644
--- a/src/tint/transform/remove_phonies.h
+++ b/src/tint/transform/remove_phonies.h
@@ -33,19 +33,10 @@
     /// Destructor
     ~RemovePhonies() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/remove_unreachable_statements.cc b/src/tint/transform/remove_unreachable_statements.cc
index 964d767..f9bf202 100644
--- a/src/tint/transform/remove_unreachable_statements.cc
+++ b/src/tint/transform/remove_unreachable_statements.cc
@@ -36,27 +36,28 @@
 
 RemoveUnreachableStatements::~RemoveUnreachableStatements() = default;
 
-bool RemoveUnreachableStatements::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* node : program->ASTNodes().Objects()) {
-        if (auto* stmt = program->Sem().Get<sem::Statement>(node)) {
+Transform::ApplyResult RemoveUnreachableStatements::Apply(const Program* src,
+                                                          const DataMap&,
+                                                          DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
+    bool made_changes = false;
+    for (auto* node : src->ASTNodes().Objects()) {
+        if (auto* stmt = src->Sem().Get<sem::Statement>(node)) {
             if (!stmt->IsReachable()) {
-                return true;
+                RemoveStatement(ctx, stmt->Declaration());
+                made_changes = true;
             }
         }
     }
-    return false;
-}
 
-void RemoveUnreachableStatements::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
-        if (auto* stmt = ctx.src->Sem().Get<sem::Statement>(node)) {
-            if (!stmt->IsReachable()) {
-                RemoveStatement(ctx, stmt->Declaration());
-            }
-        }
+    if (!made_changes) {
+        return SkipTransform;
     }
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/remove_unreachable_statements.h b/src/tint/transform/remove_unreachable_statements.h
index 7f8b947..f5848f5 100644
--- a/src/tint/transform/remove_unreachable_statements.h
+++ b/src/tint/transform/remove_unreachable_statements.h
@@ -32,19 +32,10 @@
     /// Destructor
     ~RemoveUnreachableStatements() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/renamer.cc b/src/tint/transform/renamer.cc
index 562a52f..0fd1113 100644
--- a/src/tint/transform/renamer.cc
+++ b/src/tint/transform/renamer.cc
@@ -1252,39 +1252,31 @@
 Renamer::Renamer() = default;
 Renamer::~Renamer() = default;
 
-Output Renamer::Run(const Program* in, const DataMap& inputs) const {
-    ProgramBuilder out;
-    // Disable auto-cloning of symbols, since we want to rename them.
-    CloneContext ctx(&out, in, false);
+Transform::ApplyResult Renamer::Apply(const Program* src,
+                                      const DataMap& inputs,
+                                      DataMap& outputs) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ false};
 
     // Swizzles, builtin calls and builtin structure members need to keep their
     // symbols preserved.
-    std::unordered_set<const ast::IdentifierExpression*> preserve;
-    for (auto* node : in->ASTNodes().Objects()) {
+    utils::Hashset<const ast::IdentifierExpression*, 8> preserve;
+    for (auto* node : src->ASTNodes().Objects()) {
         if (auto* member = node->As<ast::MemberAccessorExpression>()) {
-            auto* sem = in->Sem().Get(member);
-            if (!sem) {
-                TINT_ICE(Transform, out.Diagnostics())
-                    << "MemberAccessorExpression has no semantic info";
-                continue;
-            }
+            auto* sem = src->Sem().Get(member);
             if (sem->Is<sem::Swizzle>()) {
-                preserve.emplace(member->member);
-            } else if (auto* str_expr = in->Sem().Get(member->structure)) {
+                preserve.Add(member->member);
+            } else if (auto* str_expr = src->Sem().Get(member->structure)) {
                 if (auto* ty = str_expr->Type()->UnwrapRef()->As<sem::Struct>()) {
                     if (ty->Declaration() == nullptr) {  // Builtin structure
-                        preserve.emplace(member->member);
+                        preserve.Add(member->member);
                     }
                 }
             }
         } else if (auto* call = node->As<ast::CallExpression>()) {
-            auto* sem = in->Sem().Get(call)->UnwrapMaterialize()->As<sem::Call>();
-            if (!sem) {
-                TINT_ICE(Transform, out.Diagnostics()) << "CallExpression has no semantic info";
-                continue;
-            }
+            auto* sem = src->Sem().Get(call)->UnwrapMaterialize()->As<sem::Call>();
             if (sem->Target()->Is<sem::Builtin>()) {
-                preserve.emplace(call->target.name);
+                preserve.Add(call->target.name);
             }
         }
     }
@@ -1300,7 +1292,7 @@
     }
 
     ctx.ReplaceAll([&](Symbol sym_in) {
-        auto name_in = ctx.src->Symbols().NameFor(sym_in);
+        auto name_in = src->Symbols().NameFor(sym_in);
         if (preserve_unicode || text::utf8::IsASCII(name_in)) {
             switch (target) {
                 case Target::kAll:
@@ -1343,17 +1335,20 @@
     });
 
     ctx.ReplaceAll([&](const ast::IdentifierExpression* ident) -> const ast::IdentifierExpression* {
-        if (preserve.count(ident)) {
+        if (preserve.Contains(ident)) {
             auto sym_in = ident->symbol;
-            auto str = in->Symbols().NameFor(sym_in);
-            auto sym_out = out.Symbols().Register(str);
+            auto str = src->Symbols().NameFor(sym_in);
+            auto sym_out = b.Symbols().Register(str);
             return ctx.dst->create<ast::IdentifierExpression>(ctx.Clone(ident->source), sym_out);
         }
         return nullptr;  // Clone ident. Uses the symbol remapping above.
     });
-    ctx.Clone();
 
-    return Output(Program(std::move(out)), std::make_unique<Data>(std::move(remappings)));
+    ctx.Clone();  // Must come before the std::move()
+
+    outputs.Add<Data>(std::move(remappings));
+
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/renamer.h b/src/tint/transform/renamer.h
index 000aee9..8a9f97e 100644
--- a/src/tint/transform/renamer.h
+++ b/src/tint/transform/renamer.h
@@ -85,11 +85,10 @@
     /// Destructor
     ~Renamer() override;
 
-    /// Runs the transform on `program`, returning the transformation result.
-    /// @param program the source program to transform
-    /// @param data optional extra transform-specific input data
-    /// @returns the transformation result
-    Output Run(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/robustness.cc b/src/tint/transform/robustness.cc
index a22f84f..75d63f2 100644
--- a/src/tint/transform/robustness.cc
+++ b/src/tint/transform/robustness.cc
@@ -33,36 +33,48 @@
 
 namespace tint::transform {
 
-/// State holds the current transform state
+/// PIMPL state for the transform
 struct Robustness::State {
-    /// The clone context
-    CloneContext& ctx;
+    /// Constructor
+    /// @param program the source program
+    /// @param omitted the omitted address spaces
+    State(const Program* program, std::unordered_set<ast::AddressSpace>&& omitted)
+        : src(program), omitted_address_spaces(std::move(omitted)) {}
 
-    /// Set of address spacees to not apply the transform to
-    std::unordered_set<ast::AddressSpace> omitted_classes;
-
-    /// Applies the transformation state to `ctx`.
-    void Transform() {
+    /// Runs the transform
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
         ctx.ReplaceAll([&](const ast::IndexAccessorExpression* expr) { return Transform(expr); });
         ctx.ReplaceAll([&](const ast::CallExpression* expr) { return Transform(expr); });
+
+        ctx.Clone();
+        return Program(std::move(b));
     }
 
+  private:
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
+    /// The clone context
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
+
+    /// Set of address spaces to not apply the transform to
+    std::unordered_set<ast::AddressSpace> omitted_address_spaces;
+
     /// Apply bounds clamping to array, vector and matrix indexing
     /// @param expr the array, vector or matrix index expression
     /// @return the clamped replacement expression, or nullptr if `expr` should be cloned without
     /// changes.
     const ast::IndexAccessorExpression* Transform(const ast::IndexAccessorExpression* expr) {
-        auto* sem =
-            ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::IndexAccessorExpression>();
+        auto* sem = src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::IndexAccessorExpression>();
         auto* ret_type = sem->Type();
 
         auto* ref = ret_type->As<sem::Reference>();
-        if (ref && omitted_classes.count(ref->AddressSpace()) != 0) {
+        if (ref && omitted_address_spaces.count(ref->AddressSpace()) != 0) {
             return nullptr;
         }
 
-        ProgramBuilder& b = *ctx.dst;
-
         // idx return the cloned index expression, as a u32.
         auto idx = [&]() -> const ast::Expression* {
             auto* i = ctx.Clone(expr->index);
@@ -109,8 +121,8 @@
                 } else {
                     // Note: Don't be tempted to use the array override variable as an expression
                     // here, the name might be shadowed!
-                    ctx.dst->Diagnostics().add_error(diag::System::Transform,
-                                                     sem::Array::kErrExpectedConstantCount);
+                    b.Diagnostics().add_error(diag::System::Transform,
+                                              sem::Array::kErrExpectedConstantCount);
                     return nullptr;
                 }
 
@@ -119,7 +131,7 @@
             [&](Default) {
                 TINT_ICE(Transform, b.Diagnostics())
                     << "unhandled object type in robustness of array index: "
-                    << ctx.src->FriendlyName(ret_type->UnwrapRef());
+                    << src->FriendlyName(ret_type->UnwrapRef());
                 return nullptr;
             });
 
@@ -127,9 +139,9 @@
             return nullptr;  // Clamping not needed
         }
 
-        auto src = ctx.Clone(expr->source);
-        auto* obj = ctx.Clone(expr->object);
-        return b.IndexAccessor(src, obj, clamped_idx);
+        auto idx_src = ctx.Clone(expr->source);
+        auto* idx_obj = ctx.Clone(expr->object);
+        return b.IndexAccessor(idx_src, idx_obj, clamped_idx);
     }
 
     /// @param type builtin type
@@ -145,15 +157,13 @@
     /// @return the clamped replacement call expression, or nullptr if `expr`
     /// should be cloned without changes.
     const ast::CallExpression* Transform(const ast::CallExpression* expr) {
-        auto* call = ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
+        auto* call = src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
         auto* call_target = call->Target();
         auto* builtin = call_target->As<sem::Builtin>();
         if (!builtin || !TextureBuiltinNeedsClamping(builtin->Type())) {
             return nullptr;  // No transform, just clone.
         }
 
-        ProgramBuilder& b = *ctx.dst;
-
         // Indices of the mandatory texture and coords parameters, and the optional
         // array and level parameters.
         auto& signature = builtin->Signature();
@@ -261,7 +271,7 @@
         // Clamp the level argument, if provided
         if (level_idx >= 0) {
             auto* arg = expr->args[static_cast<size_t>(level_idx)];
-            ctx.Replace(arg, level_arg ? level_arg() : ctx.dst->Expr(0_a));
+            ctx.Replace(arg, level_arg ? level_arg() : b.Expr(0_a));
         }
 
         return nullptr;  // Clone, which will use the argument replacements above.
@@ -276,28 +286,27 @@
 Robustness::Robustness() = default;
 Robustness::~Robustness() = default;
 
-void Robustness::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const {
+Transform::ApplyResult Robustness::Apply(const Program* src,
+                                         const DataMap& inputs,
+                                         DataMap&) const {
     Config cfg;
     if (auto* cfg_data = inputs.Get<Config>()) {
         cfg = *cfg_data;
     }
 
-    std::unordered_set<ast::AddressSpace> omitted_classes;
-    for (auto sc : cfg.omitted_classes) {
+    std::unordered_set<ast::AddressSpace> omitted_address_spaces;
+    for (auto sc : cfg.omitted_address_spaces) {
         switch (sc) {
             case AddressSpace::kUniform:
-                omitted_classes.insert(ast::AddressSpace::kUniform);
+                omitted_address_spaces.insert(ast::AddressSpace::kUniform);
                 break;
             case AddressSpace::kStorage:
-                omitted_classes.insert(ast::AddressSpace::kStorage);
+                omitted_address_spaces.insert(ast::AddressSpace::kStorage);
                 break;
         }
     }
 
-    State state{ctx, std::move(omitted_classes)};
-
-    state.Transform();
-    ctx.Clone();
+    return State{src, std::move(omitted_address_spaces)}.Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/robustness.h b/src/tint/transform/robustness.h
index 21a7ff9..14c5fe1 100644
--- a/src/tint/transform/robustness.h
+++ b/src/tint/transform/robustness.h
@@ -54,9 +54,9 @@
         /// @returns this Config
         Config& operator=(const Config&);
 
-        /// Address spacees to omit from apply the transform to.
+        /// Address spaces to omit from apply the transform to.
         /// This allows for optimizing on hardware that provide safe accesses.
-        std::unordered_set<AddressSpace> omitted_classes;
+        std::unordered_set<AddressSpace> omitted_address_spaces;
     };
 
     /// Constructor
@@ -64,14 +64,10 @@
     /// Destructor
     ~Robustness() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
     struct State;
diff --git a/src/tint/transform/robustness_test.cc b/src/tint/transform/robustness_test.cc
index 16d958f..990bbde 100644
--- a/src/tint/transform/robustness_test.cc
+++ b/src/tint/transform/robustness_test.cc
@@ -1274,7 +1274,7 @@
 )";
 
     Robustness::Config cfg;
-    cfg.omitted_classes.insert(Robustness::AddressSpace::kStorage);
+    cfg.omitted_address_spaces.insert(Robustness::AddressSpace::kStorage);
 
     DataMap data;
     data.Add<Robustness::Config>(cfg);
@@ -1325,7 +1325,7 @@
 )";
 
     Robustness::Config cfg;
-    cfg.omitted_classes.insert(Robustness::AddressSpace::kUniform);
+    cfg.omitted_address_spaces.insert(Robustness::AddressSpace::kUniform);
 
     DataMap data;
     data.Add<Robustness::Config>(cfg);
@@ -1376,8 +1376,8 @@
 )";
 
     Robustness::Config cfg;
-    cfg.omitted_classes.insert(Robustness::AddressSpace::kStorage);
-    cfg.omitted_classes.insert(Robustness::AddressSpace::kUniform);
+    cfg.omitted_address_spaces.insert(Robustness::AddressSpace::kStorage);
+    cfg.omitted_address_spaces.insert(Robustness::AddressSpace::kUniform);
 
     DataMap data;
     data.Add<Robustness::Config>(cfg);
diff --git a/src/tint/transform/simplify_pointers.cc b/src/tint/transform/simplify_pointers.cc
index ea35699..b2b99ed 100644
--- a/src/tint/transform/simplify_pointers.cc
+++ b/src/tint/transform/simplify_pointers.cc
@@ -45,14 +45,18 @@
 
 }  // namespace
 
-/// The PIMPL state for the SimplifyPointers transform
+/// PIMPL state for the transform
 struct SimplifyPointers::State {
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
     /// The clone context
-    CloneContext& ctx;
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
 
     /// Constructor
-    /// @param context the clone context
-    explicit State(CloneContext& context) : ctx(context) {}
+    /// @param program the source program
+    explicit State(const Program* program) : src(program) {}
 
     /// Traverses the expression `expr` looking for non-literal array indexing
     /// expressions that would affect the computed address of a pointer
@@ -120,10 +124,11 @@
         }
     }
 
-    /// Performs the transformation
-    void Run() {
+    /// Runs the transform
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
         // A map of saved expressions to their saved variable name
-        std::unordered_map<const ast::Expression*, Symbol> saved_vars;
+        utils::Hashmap<const ast::Expression*, Symbol, 8> saved_vars;
 
         // Register the ast::Expression transform handler.
         // This performs two different transformations:
@@ -135,9 +140,8 @@
         // variable identifier.
         ctx.ReplaceAll([&](const ast::Expression* expr) -> const ast::Expression* {
             // Look to see if we need to swap this Expression with a saved variable.
-            auto it = saved_vars.find(expr);
-            if (it != saved_vars.end()) {
-                return ctx.dst->Expr(it->second);
+            if (auto* saved_var = saved_vars.Find(expr)) {
+                return ctx.dst->Expr(*saved_var);
             }
 
             // Reduce the expression, folding away chains of address-of / indirections
@@ -174,7 +178,7 @@
 
                 // Scan the initializer expression for array index expressions that need
                 // to be hoist to temporary "saved" variables.
-                std::vector<const ast::VariableDeclStatement*> saved;
+                utils::Vector<const ast::VariableDeclStatement*, 8> saved;
                 CollectSavedArrayIndices(
                     var->Declaration()->initializer, [&](const ast::Expression* idx_expr) {
                         // We have a sub-expression that needs to be saved.
@@ -182,18 +186,18 @@
                         auto saved_name = ctx.dst->Symbols().New(
                             ctx.src->Symbols().NameFor(var->Declaration()->symbol) + "_save");
                         auto* decl = ctx.dst->Decl(ctx.dst->Let(saved_name, ctx.Clone(idx_expr)));
-                        saved.emplace_back(decl);
+                        saved.Push(decl);
                         // Record the substitution of `idx_expr` to the saved variable
                         // with the symbol `saved_name`. This will be used by the
                         // ReplaceAll() handler above.
-                        saved_vars.emplace(idx_expr, saved_name);
+                        saved_vars.Add(idx_expr, saved_name);
                     });
 
                 // Find the place to insert the saved declarations.
                 // Special care needs to be made for lets declared as the initializer
                 // part of for-loops. In this case the block will hold the for-loop
                 // statement, not the let.
-                if (!saved.empty()) {
+                if (!saved.IsEmpty()) {
                     auto* stmt = ctx.src->Sem().Get(let);
                     auto* block = stmt->Block();
                     // Find the statement owned by the block (either the let decl or a
@@ -219,7 +223,9 @@
                 RemoveStatement(ctx, let);
             }
         }
+
         ctx.Clone();
+        return Program(std::move(b));
     }
 };
 
@@ -227,8 +233,8 @@
 
 SimplifyPointers::~SimplifyPointers() = default;
 
-void SimplifyPointers::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    State(ctx).Run();
+Transform::ApplyResult SimplifyPointers::Apply(const Program* src, const DataMap&, DataMap&) const {
+    return State(src).Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/simplify_pointers.h b/src/tint/transform/simplify_pointers.h
index 787c7d8..6e040bb 100644
--- a/src/tint/transform/simplify_pointers.h
+++ b/src/tint/transform/simplify_pointers.h
@@ -39,16 +39,13 @@
     /// Destructor
     ~SimplifyPointers() override;
 
-  protected:
-    struct State;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+  private:
+    struct State;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/single_entry_point.cc b/src/tint/transform/single_entry_point.cc
index 8d26a7f..87787ae 100644
--- a/src/tint/transform/single_entry_point.cc
+++ b/src/tint/transform/single_entry_point.cc
@@ -30,33 +30,37 @@
 
 SingleEntryPoint::~SingleEntryPoint() = default;
 
-void SingleEntryPoint::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const {
+Transform::ApplyResult SingleEntryPoint::Apply(const Program* src,
+                                               const DataMap& inputs,
+                                               DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     auto* cfg = inputs.Get<Config>();
     if (cfg == nullptr) {
-        ctx.dst->Diagnostics().add_error(
-            diag::System::Transform, "missing transform data for " + std::string(TypeInfo().name));
-
-        return;
+        b.Diagnostics().add_error(diag::System::Transform,
+                                  "missing transform data for " + std::string(TypeInfo().name));
+        return Program(std::move(b));
     }
 
     // Find the target entry point.
     const ast::Function* entry_point = nullptr;
-    for (auto* f : ctx.src->AST().Functions()) {
+    for (auto* f : src->AST().Functions()) {
         if (!f->IsEntryPoint()) {
             continue;
         }
-        if (ctx.src->Symbols().NameFor(f->symbol) == cfg->entry_point_name) {
+        if (src->Symbols().NameFor(f->symbol) == cfg->entry_point_name) {
             entry_point = f;
             break;
         }
     }
     if (entry_point == nullptr) {
-        ctx.dst->Diagnostics().add_error(diag::System::Transform,
-                                         "entry point '" + cfg->entry_point_name + "' not found");
-        return;
+        b.Diagnostics().add_error(diag::System::Transform,
+                                  "entry point '" + cfg->entry_point_name + "' not found");
+        return Program(std::move(b));
     }
 
-    auto& sem = ctx.src->Sem();
+    auto& sem = src->Sem();
 
     // Build set of referenced module-scope variables for faster lookups later.
     std::unordered_set<const ast::Variable*> referenced_vars;
@@ -66,12 +70,12 @@
 
     // Clone any module-scope variables, types, and functions that are statically referenced by the
     // target entry point.
-    for (auto* decl : ctx.src->AST().GlobalDeclarations()) {
+    for (auto* decl : src->AST().GlobalDeclarations()) {
         Switch(
             decl,  //
             [&](const ast::TypeDecl* ty) {
                 // TODO(jrprice): Strip unused types.
-                ctx.dst->AST().AddTypeDecl(ctx.Clone(ty));
+                b.AST().AddTypeDecl(ctx.Clone(ty));
             },
             [&](const ast::Override* override) {
                 if (referenced_vars.count(override)) {
@@ -80,37 +84,39 @@
                         // so that its allocated ID so that it won't be affected by other
                         // stripped away overrides
                         auto* global = sem.Get(override);
-                        const auto* id = ctx.dst->Id(global->OverrideId());
+                        const auto* id = b.Id(global->OverrideId());
                         ctx.InsertFront(override->attributes, id);
                     }
-                    ctx.dst->AST().AddGlobalVariable(ctx.Clone(override));
+                    b.AST().AddGlobalVariable(ctx.Clone(override));
                 }
             },
             [&](const ast::Var* var) {
                 if (referenced_vars.count(var)) {
-                    ctx.dst->AST().AddGlobalVariable(ctx.Clone(var));
+                    b.AST().AddGlobalVariable(ctx.Clone(var));
                 }
             },
             [&](const ast::Const* c) {
                 // Always keep 'const' declarations, as these can be used by attributes and array
                 // sizes, which are not tracked as transitively used by functions. They also don't
                 // typically get emitted by the backend unless they're actually used.
-                ctx.dst->AST().AddGlobalVariable(ctx.Clone(c));
+                b.AST().AddGlobalVariable(ctx.Clone(c));
             },
             [&](const ast::Function* func) {
                 if (sem.Get(func)->HasAncestorEntryPoint(entry_point->symbol)) {
-                    ctx.dst->AST().AddFunction(ctx.Clone(func));
+                    b.AST().AddFunction(ctx.Clone(func));
                 }
             },
-            [&](const ast::Enable* ext) { ctx.dst->AST().AddEnable(ctx.Clone(ext)); },
+            [&](const ast::Enable* ext) { b.AST().AddEnable(ctx.Clone(ext)); },
             [&](Default) {
-                TINT_UNREACHABLE(Transform, ctx.dst->Diagnostics())
+                TINT_UNREACHABLE(Transform, b.Diagnostics())
                     << "unhandled global declaration: " << decl->TypeInfo().name;
             });
     }
 
     // Clone the entry point.
-    ctx.dst->AST().AddFunction(ctx.Clone(entry_point));
+    b.AST().AddFunction(ctx.Clone(entry_point));
+
+    return Program(std::move(b));
 }
 
 SingleEntryPoint::Config::Config(std::string entry_point) : entry_point_name(entry_point) {}
diff --git a/src/tint/transform/single_entry_point.h b/src/tint/transform/single_entry_point.h
index 59aa021..7aba5e8 100644
--- a/src/tint/transform/single_entry_point.h
+++ b/src/tint/transform/single_entry_point.h
@@ -53,14 +53,10 @@
     /// Destructor
     ~SingleEntryPoint() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/spirv_atomic.cc b/src/tint/transform/spirv_atomic.cc
index 127c702..eba6681 100644
--- a/src/tint/transform/spirv_atomic.cc
+++ b/src/tint/transform/spirv_atomic.cc
@@ -37,7 +37,7 @@
 
 using namespace tint::number_suffixes;  // NOLINT
 
-/// Private implementation of transform
+/// PIMPL state for the transform
 struct SpirvAtomic::State {
   private:
     /// A struct that has been forked because a subset of members were made atomic.
@@ -46,19 +46,24 @@
         std::unordered_set<size_t> atomic_members;
     };
 
-    CloneContext& ctx;
-    ProgramBuilder& b = *ctx.dst;
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
+    /// The clone context
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
     std::unordered_map<const ast::Struct*, ForkedStruct> forked_structs;
     std::unordered_set<const sem::Variable*> atomic_variables;
     utils::UniqueVector<const sem::Expression*, 8> atomic_expressions;
 
   public:
     /// Constructor
-    /// @param c the clone context
-    explicit State(CloneContext& c) : ctx(c) {}
+    /// @param program the source program
+    explicit State(const Program* program) : src(program) {}
 
     /// Runs the transform
-    void Run() {
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
         // Look for stub functions generated by the SPIR-V reader, which are used as placeholders
         // for atomic builtin calls.
         for (auto* fn : ctx.src->AST().Functions()) {
@@ -102,6 +107,10 @@
             }
         }
 
+        if (atomic_expressions.IsEmpty()) {
+            return SkipTransform;
+        }
+
         // Transform all variables and structure members that were used in atomic operations as
         // atomic types. This propagates up originating expression chains.
         ProcessAtomicExpressions();
@@ -143,6 +152,7 @@
         ReplaceLoadsAndStores();
 
         ctx.Clone();
+        return Program(std::move(b));
     }
 
   private:
@@ -297,17 +307,8 @@
                                                           ctx->dst->AllocateNodeID(), builtin);
 }
 
-bool SpirvAtomic::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* fn : program->AST().Functions()) {
-        if (ast::HasAttribute<Stub>(fn->attributes)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void SpirvAtomic::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    State{ctx}.Run();
+Transform::ApplyResult SpirvAtomic::Apply(const Program* src, const DataMap&, DataMap&) const {
+    return State{src}.Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/spirv_atomic.h b/src/tint/transform/spirv_atomic.h
index e1311c5..0f99dba 100644
--- a/src/tint/transform/spirv_atomic.h
+++ b/src/tint/transform/spirv_atomic.h
@@ -63,21 +63,13 @@
         const sem::BuiltinType builtin;
     };
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-  protected:
+  private:
     struct State;
-
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/std140.cc b/src/tint/transform/std140.cc
index 920fc69..0746bda 100644
--- a/src/tint/transform/std140.cc
+++ b/src/tint/transform/std140.cc
@@ -77,14 +77,20 @@
 
 namespace tint::transform {
 
-/// The PIMPL state for the Std140 transform
+/// PIMPL state for the transform
 struct Std140::State {
     /// Constructor
-    /// @param c the CloneContext
-    explicit State(CloneContext& c) : ctx(c) {}
+    /// @param program the source program
+    explicit State(const Program* program) : src(program) {}
 
     /// Runs the transform
-    void Run() {
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
+        if (!ShouldRun()) {
+            // Transform is not required
+            return SkipTransform;
+        }
+
         // Begin by creating forked types for any type that is used as a uniform buffer, that
         // either directly or transitively contains a matrix that needs splitting for std140 layout.
         ForkTypes();
@@ -116,11 +122,11 @@
         });
 
         ctx.Clone();
+        return Program(std::move(b));
     }
 
     /// @returns true if this transform should be run for the given program
-    /// @param program the program to inspect
-    static bool ShouldRun(const Program* program) {
+    bool ShouldRun() const {
         // Returns true if the type needs to be forked for std140 usage.
         auto needs_fork = [&](const sem::Type* ty) {
             while (auto* arr = ty->As<sem::Array>()) {
@@ -135,7 +141,7 @@
         };
 
         // Scan structures for members that need forking
-        for (auto* ty : program->Types()) {
+        for (auto* ty : src->Types()) {
             if (auto* str = ty->As<sem::Struct>()) {
                 if (str->UsedAs(ast::AddressSpace::kUniform)) {
                     for (auto* member : str->Members()) {
@@ -148,8 +154,8 @@
         }
 
         // Scan uniform variables that have types that need forking
-        for (auto* decl : program->AST().GlobalVariables()) {
-            auto* global = program->Sem().Get(decl);
+        for (auto* decl : src->AST().GlobalVariables()) {
+            auto* global = src->Sem().Get(decl);
             if (global->AddressSpace() == ast::AddressSpace::kUniform) {
                 if (needs_fork(global->Type()->UnwrapRef())) {
                     return true;
@@ -197,14 +203,16 @@
         }
     };
 
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
     /// The clone context
-    CloneContext& ctx;
-    /// Alias to the semantic info in ctx.src
-    const sem::Info& sem = ctx.src->Sem();
-    /// Alias to the symbols in ctx.src
-    const SymbolTable& sym = ctx.src->Symbols();
-    /// Alias to the ctx.dst program builder
-    ProgramBuilder& b = *ctx.dst;
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
+    /// Alias to the semantic info in src
+    const sem::Info& sem = src->Sem();
+    /// Alias to the symbols in src
+    const SymbolTable& sym = src->Symbols();
 
     /// Map of load function signature, to the generated function
     utils::Hashmap<LoadFnKey, Symbol, 8, LoadFnKey::Hasher> load_fns;
@@ -218,7 +226,7 @@
     // Map of original structure to 'std140' forked structure
     utils::Hashmap<const sem::Struct*, Symbol, 8> std140_structs;
 
-    // Map of structure member in ctx.src of a matrix type, to list of decomposed column
+    // Map of structure member in src of a matrix type, to list of decomposed column
     // members in ctx.dst.
     utils::Hashmap<const sem::StructMember*, utils::Vector<const ast::StructMember*, 4>, 8>
         std140_mat_members;
@@ -232,7 +240,7 @@
         utils::Vector<Symbol, 4> columns;
     };
 
-    // Map of matrix type in ctx.src, to decomposed column structure in ctx.dst.
+    // Map of matrix type in src, to decomposed column structure in ctx.dst.
     utils::Hashmap<const sem::Matrix*, Std140Matrix, 8> std140_mats;
 
     /// AccessChain describes a chain of access expressions to uniform buffer variable.
@@ -266,7 +274,7 @@
     /// map (via Std140Type()).
     void ForkTypes() {
         // For each module scope declaration...
-        for (auto* global : ctx.src->Sem().Module()->DependencyOrderedDeclarations()) {
+        for (auto* global : src->Sem().Module()->DependencyOrderedDeclarations()) {
             // Check to see if this is a structure used by a uniform buffer...
             auto* str = sem.Get<sem::Struct>(global);
             if (str && str->UsedAs(ast::AddressSpace::kUniform)) {
@@ -317,7 +325,7 @@
                 if (fork_std140) {
                     // Clone any members that have not already been cloned.
                     for (auto& member : members) {
-                        if (member->program_id == ctx.src->ID()) {
+                        if (member->program_id == src->ID()) {
                             member = ctx.Clone(member);
                         }
                     }
@@ -326,7 +334,7 @@
                     auto name = b.Symbols().New(sym.NameFor(str->Name()) + "_std140");
                     auto* std140 = b.create<ast::Struct>(name, std::move(members),
                                                          ctx.Clone(str->Declaration()->attributes));
-                    ctx.InsertAfter(ctx.src->AST().GlobalDeclarations(), global, std140);
+                    ctx.InsertAfter(src->AST().GlobalDeclarations(), global, std140);
                     std140_structs.Add(str, name);
                 }
             }
@@ -337,14 +345,13 @@
     /// type that has been forked for std140-layout.
     /// Populates the #std140_uniforms set.
     void ReplaceUniformVarTypes() {
-        for (auto* global : ctx.src->AST().GlobalVariables()) {
+        for (auto* global : src->AST().GlobalVariables()) {
             if (auto* var = global->As<ast::Var>()) {
                 if (var->declared_address_space == ast::AddressSpace::kUniform) {
                     auto* v = sem.Get(var);
                     if (auto* std140_ty = Std140Type(v->Type()->UnwrapRef())) {
                         ctx.Replace(global->type, std140_ty);
                         std140_uniforms.Add(v);
-                        continue;
                     }
                 }
             }
@@ -404,7 +411,7 @@
                     auto std140_mat = std140_mats.GetOrCreate(mat, [&] {
                         auto name = b.Symbols().New("mat" + std::to_string(mat->columns()) + "x" +
                                                     std::to_string(mat->rows()) + "_" +
-                                                    ctx.src->FriendlyName(mat->type()));
+                                                    src->FriendlyName(mat->type()));
                         auto members =
                             DecomposedMatrixStructMembers(mat, "col", mat->Align(), mat->Size());
                         b.Structure(name, members);
@@ -421,7 +428,7 @@
                 if (auto* std140 = Std140Type(arr->ElemType())) {
                     utils::Vector<const ast::Attribute*, 1> attrs;
                     if (!arr->IsStrideImplicit()) {
-                        attrs.Push(ctx.dst->create<ast::StrideAttribute>(arr->Stride()));
+                        attrs.Push(b.create<ast::StrideAttribute>(arr->Stride()));
                     }
                     auto count = arr->ConstantCount();
                     if (!count) {
@@ -429,7 +436,7 @@
                         // * Override-expression counts can only be applied to workgroup arrays, and
                         //   this method only handles types transitively used as uniform buffers.
                         // * Runtime-sized arrays cannot be used in uniform buffers.
-                        TINT_ICE(Transform, ctx.dst->Diagnostics())
+                        TINT_ICE(Transform, b.Diagnostics())
                             << "unexpected non-constant array count";
                         count = 1;
                     }
@@ -440,7 +447,7 @@
             });
     }
 
-    /// @param mat the matrix to decompose (in ctx.src)
+    /// @param mat the matrix to decompose (in src)
     /// @param name_prefix the name prefix to apply to each of the returned column vector members.
     /// @param align the alignment in bytes of the matrix.
     /// @param size the size in bytes of the matrix.
@@ -473,7 +480,7 @@
             // Build the member
             const auto col_name = name_prefix + std::to_string(i);
             const auto* col_ty = CreateASTTypeFor(ctx, mat->ColumnType());
-            const auto* col_member = ctx.dst->Member(col_name, col_ty, std::move(attributes));
+            const auto* col_member = b.Member(col_name, col_ty, std::move(attributes));
             // Record the member for std140_mat_members
             out.Push(col_member);
         }
@@ -618,7 +625,7 @@
 
     /// @returns a name suffix for a std140 -> non-std140 conversion function based on the type
     ///          being converted.
-    const std::string ConvertSuffix(const sem::Type* ty) const {
+    const std::string ConvertSuffix(const sem::Type* ty) {
         return Switch(
             ty,  //
             [&](const sem::Struct* str) { return sym.NameFor(str->Name()); },
@@ -629,8 +636,7 @@
                     // * Override-expression counts can only be applied to workgroup arrays, and
                     //   this method only handles types transitively used as uniform buffers.
                     // * Runtime-sized arrays cannot be used in uniform buffers.
-                    TINT_ICE(Transform, ctx.dst->Diagnostics())
-                        << "unexpected non-constant array count";
+                    TINT_ICE(Transform, b.Diagnostics()) << "unexpected non-constant array count";
                     count = 1;
                 }
                 return "arr" + std::to_string(count.value()) + "_" + ConvertSuffix(arr->ElemType());
@@ -642,7 +648,7 @@
             [&](const sem::F32*) { return "f32"; },
             [&](Default) {
                 TINT_ICE(Transform, b.Diagnostics())
-                    << "unhandled type for conversion name: " << ctx.src->FriendlyName(ty);
+                    << "unhandled type for conversion name: " << src->FriendlyName(ty);
                 return "";
             });
     }
@@ -718,8 +724,7 @@
                         stmts.Push(b.Return(b.Construct(mat_ty, std::move(mat_args))));
                     } else {
                         TINT_ICE(Transform, b.Diagnostics())
-                            << "failed to find std140 matrix info for: "
-                            << ctx.src->FriendlyName(ty);
+                            << "failed to find std140 matrix info for: " << src->FriendlyName(ty);
                     }
                 },  //
                 [&](const sem::Array* arr) {
@@ -736,7 +741,7 @@
                         // * Override-expression counts can only be applied to workgroup arrays, and
                         //   this method only handles types transitively used as uniform buffers.
                         // * Runtime-sized arrays cannot be used in uniform buffers.
-                        TINT_ICE(Transform, ctx.dst->Diagnostics())
+                        TINT_ICE(Transform, b.Diagnostics())
                             << "unexpected non-constant array count";
                         count = 1;
                     }
@@ -749,7 +754,7 @@
                 },
                 [&](Default) {
                     TINT_ICE(Transform, b.Diagnostics())
-                        << "unhandled type for conversion: " << ctx.src->FriendlyName(ty);
+                        << "unhandled type for conversion: " << src->FriendlyName(ty);
                 });
 
             // Generate the function
@@ -1063,7 +1068,7 @@
 
         if (std::get_if<UniformVariable>(&access)) {
             const auto* expr = b.Expr(ctx.Clone(chain.var->Declaration()->symbol));
-            const auto name = ctx.src->Symbols().NameFor(chain.var->Declaration()->symbol);
+            const auto name = src->Symbols().NameFor(chain.var->Declaration()->symbol);
             ty = chain.var->Type()->UnwrapRef();
             return {expr, ty, name};
         }
@@ -1090,7 +1095,7 @@
                 },  //
                 [&](Default) -> ExprTypeName {
                     TINT_ICE(Transform, b.Diagnostics())
-                        << "unhandled type for access chain: " << ctx.src->FriendlyName(ty);
+                        << "unhandled type for access chain: " << src->FriendlyName(ty);
                     return {};
                 });
         }
@@ -1104,14 +1109,14 @@
                     for (auto el : *swizzle) {
                         rhs += xyzw[el];
                     }
-                    auto swizzle_ty = ctx.src->Types().Find<sem::Vector>(
+                    auto swizzle_ty = src->Types().Find<sem::Vector>(
                         vec->type(), static_cast<uint32_t>(swizzle->Length()));
                     auto* expr = b.MemberAccessor(lhs, rhs);
                     return {expr, swizzle_ty, rhs};
                 },  //
                 [&](Default) -> ExprTypeName {
                     TINT_ICE(Transform, b.Diagnostics())
-                        << "unhandled type for access chain: " << ctx.src->FriendlyName(ty);
+                        << "unhandled type for access chain: " << src->FriendlyName(ty);
                     return {};
                 });
         }
@@ -1140,7 +1145,7 @@
             },  //
             [&](Default) -> ExprTypeName {
                 TINT_ICE(Transform, b.Diagnostics())
-                    << "unhandled type for access chain: " << ctx.src->FriendlyName(ty);
+                    << "unhandled type for access chain: " << src->FriendlyName(ty);
                 return {};
             });
     }
@@ -1150,12 +1155,8 @@
 
 Std140::~Std140() = default;
 
-bool Std140::ShouldRun(const Program* program, const DataMap&) const {
-    return State::ShouldRun(program);
-}
-
-void Std140::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    State(ctx).Run();
+Transform::ApplyResult Std140::Apply(const Program* src, const DataMap&, DataMap&) const {
+    return State(src).Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/std140.h b/src/tint/transform/std140.h
index ec5cad5..49e663d 100644
--- a/src/tint/transform/std140.h
+++ b/src/tint/transform/std140.h
@@ -34,21 +34,13 @@
     /// Destructor
     ~Std140() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
     struct State;
-
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/substitute_override.cc b/src/tint/transform/substitute_override.cc
index 2de04e0..7c2d0a2 100644
--- a/src/tint/transform/substitute_override.cc
+++ b/src/tint/transform/substitute_override.cc
@@ -15,6 +15,7 @@
 #include "src/tint/transform/substitute_override.h"
 
 #include <functional>
+#include <utility>
 
 #include "src/tint/program_builder.h"
 #include "src/tint/sem/builtin.h"
@@ -25,12 +26,9 @@
 TINT_INSTANTIATE_TYPEINFO(tint::transform::SubstituteOverride::Config);
 
 namespace tint::transform {
+namespace {
 
-SubstituteOverride::SubstituteOverride() = default;
-
-SubstituteOverride::~SubstituteOverride() = default;
-
-bool SubstituteOverride::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     for (auto* node : program->AST().GlobalVariables()) {
         if (node->Is<ast::Override>()) {
             return true;
@@ -39,18 +37,32 @@
     return false;
 }
 
-void SubstituteOverride::Run(CloneContext& ctx, const DataMap& config, DataMap&) const {
+}  // namespace
+
+SubstituteOverride::SubstituteOverride() = default;
+
+SubstituteOverride::~SubstituteOverride() = default;
+
+Transform::ApplyResult SubstituteOverride::Apply(const Program* src,
+                                                 const DataMap& config,
+                                                 DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     const auto* data = config.Get<Config>();
     if (!data) {
-        ctx.dst->Diagnostics().add_error(diag::System::Transform,
-                                         "Missing override substitution data");
-        return;
+        b.Diagnostics().add_error(diag::System::Transform, "Missing override substitution data");
+        return Program(std::move(b));
+    }
+
+    if (!ShouldRun(ctx.src)) {
+        return SkipTransform;
     }
 
     ctx.ReplaceAll([&](const ast::Override* w) -> const ast::Const* {
         auto* sem = ctx.src->Sem().Get(w);
 
-        auto src = ctx.Clone(w->source);
+        auto source = ctx.Clone(w->source);
         auto sym = ctx.Clone(w->symbol);
         auto* ty = ctx.Clone(w->type);
 
@@ -58,30 +70,30 @@
         auto iter = data->map.find(sem->OverrideId());
         if (iter == data->map.end()) {
             if (!w->initializer) {
-                ctx.dst->Diagnostics().add_error(
+                b.Diagnostics().add_error(
                     diag::System::Transform,
                     "Initializer not provided for override, and override not overridden.");
                 return nullptr;
             }
-            return ctx.dst->Const(src, sym, ty, ctx.Clone(w->initializer));
+            return b.Const(source, sym, ty, ctx.Clone(w->initializer));
         }
 
         auto value = iter->second;
         auto* ctor = Switch(
             sem->Type(),
-            [&](const sem::Bool*) { return ctx.dst->Expr(!std::equal_to<double>()(value, 0.0)); },
-            [&](const sem::I32*) { return ctx.dst->Expr(i32(value)); },
-            [&](const sem::U32*) { return ctx.dst->Expr(u32(value)); },
-            [&](const sem::F32*) { return ctx.dst->Expr(f32(value)); },
-            [&](const sem::F16*) { return ctx.dst->Expr(f16(value)); });
+            [&](const sem::Bool*) { return b.Expr(!std::equal_to<double>()(value, 0.0)); },
+            [&](const sem::I32*) { return b.Expr(i32(value)); },
+            [&](const sem::U32*) { return b.Expr(u32(value)); },
+            [&](const sem::F32*) { return b.Expr(f32(value)); },
+            [&](const sem::F16*) { return b.Expr(f16(value)); });
 
         if (!ctor) {
-            ctx.dst->Diagnostics().add_error(diag::System::Transform,
-                                             "Failed to create override-expression");
+            b.Diagnostics().add_error(diag::System::Transform,
+                                      "Failed to create override-expression");
             return nullptr;
         }
 
-        return ctx.dst->Const(src, sym, ty, ctor);
+        return b.Const(source, sym, ty, ctor);
     });
 
     // Ensure that objects that are indexed with an override-expression are materialized.
@@ -89,11 +101,10 @@
     // resulting type of the index may change. See: crbug.com/tint/1697.
     ctx.ReplaceAll(
         [&](const ast::IndexAccessorExpression* expr) -> const ast::IndexAccessorExpression* {
-            if (auto* sem = ctx.src->Sem().Get(expr)) {
+            if (auto* sem = src->Sem().Get(expr)) {
                 if (auto* access = sem->UnwrapMaterialize()->As<sem::IndexAccessorExpression>()) {
                     if (access->Object()->UnwrapMaterialize()->Type()->HoldsAbstract() &&
                         access->Index()->Stage() == sem::EvaluationStage::kOverride) {
-                        auto& b = *ctx.dst;
                         auto* obj = b.Call(sem::str(sem::BuiltinType::kTintMaterialize),
                                            ctx.Clone(expr->object));
                         return b.IndexAccessor(obj, ctx.Clone(expr->index));
@@ -104,6 +115,7 @@
         });
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 SubstituteOverride::Config::Config() = default;
diff --git a/src/tint/transform/substitute_override.h b/src/tint/transform/substitute_override.h
index 940e11d..853acc7 100644
--- a/src/tint/transform/substitute_override.h
+++ b/src/tint/transform/substitute_override.h
@@ -75,19 +75,10 @@
     /// Destructor
     ~SubstituteOverride() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/test_helper.h b/src/tint/transform/test_helper.h
index bc82fe5..ac48c5f 100644
--- a/src/tint/transform/test_helper.h
+++ b/src/tint/transform/test_helper.h
@@ -122,7 +122,18 @@
         }
 
         const Transform& t = TRANSFORM();
-        return t.ShouldRun(&program, data);
+
+        DataMap outputs;
+        auto result = t.Apply(&program, data, outputs);
+        if (!result) {
+            return false;
+        }
+        if (!result->IsValid()) {
+            ADD_FAILURE() << "Apply() called by ShouldRun() returned errors: "
+                          << result->Diagnostics().str();
+            return true;
+        }
+        return result.has_value();
     }
 
     /// @param in the input WGSL source
diff --git a/src/tint/transform/transform.cc b/src/tint/transform/transform.cc
index 3e03411..c37f3b4 100644
--- a/src/tint/transform/transform.cc
+++ b/src/tint/transform/transform.cc
@@ -46,24 +46,19 @@
 Transform::Transform() = default;
 Transform::~Transform() = default;
 
-Output Transform::Run(const Program* program, const DataMap& data /* = {} */) const {
-    ProgramBuilder builder;
-    CloneContext ctx(&builder, program);
+Output Transform::Run(const Program* src, const DataMap& data /* = {} */) const {
     Output output;
-    Run(ctx, data, output.data);
-    output.program = Program(std::move(builder));
+    if (auto program = Apply(src, data, output.data)) {
+        output.program = std::move(program.value());
+    } else {
+        ProgramBuilder b;
+        CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+        ctx.Clone();
+        output.program = Program(std::move(b));
+    }
     return output;
 }
 
-void Transform::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    TINT_UNIMPLEMENTED(Transform, ctx.dst->Diagnostics())
-        << "Transform::Run() unimplemented for " << TypeInfo().name;
-}
-
-bool Transform::ShouldRun(const Program*, const DataMap&) const {
-    return true;
-}
-
 void Transform::RemoveStatement(CloneContext& ctx, const ast::Statement* stmt) {
     auto* sem = ctx.src->Sem().Get(stmt);
     if (auto* block = tint::As<sem::BlockStatement>(sem->Parent())) {
diff --git a/src/tint/transform/transform.h b/src/tint/transform/transform.h
index c3e3d1d..6580e25 100644
--- a/src/tint/transform/transform.h
+++ b/src/tint/transform/transform.h
@@ -158,26 +158,30 @@
     /// Destructor
     ~Transform() override;
 
-    /// Runs the transform on `program`, returning the transformation result.
+    /// Runs the transform on @p program, returning the transformation result or a clone of
+    /// @p program.
     /// @param program the source program to transform
     /// @param data optional extra transform-specific input data
     /// @returns the transformation result
-    virtual Output Run(const Program* program, const DataMap& data = {}) const;
+    Output Run(const Program* program, const DataMap& data = {}) const;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    virtual bool ShouldRun(const Program* program, const DataMap& data = {}) const;
+    /// The return value of Apply().
+    /// If SkipTransform (std::nullopt), then the transform is not needed to be run.
+    using ApplyResult = std::optional<Program>;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
+    /// Value returned from Apply() to indicate that the transform does not need to be run
+    static inline constexpr std::nullopt_t SkipTransform = std::nullopt;
+
+    /// Runs the transform on `program`, return.
+    /// @param program the input program
     /// @param inputs optional extra transform-specific input data
     /// @param outputs optional extra transform-specific output data
-    virtual void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const;
+    /// @returns a transformed program, or std::nullopt if the transform didn't need to run.
+    virtual ApplyResult Apply(const Program* program,
+                              const DataMap& inputs,
+                              DataMap& outputs) const = 0;
 
+  protected:
     /// Removes the statement `stmt` from the transformed program.
     /// RemoveStatement handles edge cases, like statements in the initializer and
     /// continuing of for-loops.
diff --git a/src/tint/transform/transform_test.cc b/src/tint/transform/transform_test.cc
index d063ba2..82fdf6a 100644
--- a/src/tint/transform/transform_test.cc
+++ b/src/tint/transform/transform_test.cc
@@ -23,7 +23,9 @@
 
 // Inherit from Transform so we have access to protected methods
 struct CreateASTTypeForTest : public testing::Test, public Transform {
-    Output Run(const Program*, const DataMap&) const override { return {}; }
+    ApplyResult Apply(const Program*, const DataMap&, DataMap&) const override {
+        return SkipTransform;
+    }
 
     const ast::Type* create(std::function<sem::Type*(ProgramBuilder&)> create_sem_type) {
         ProgramBuilder sem_type_builder;
diff --git a/src/tint/transform/unshadow.cc b/src/tint/transform/unshadow.cc
index 975e2ed..93ce595 100644
--- a/src/tint/transform/unshadow.cc
+++ b/src/tint/transform/unshadow.cc
@@ -28,27 +28,32 @@
 
 namespace tint::transform {
 
-/// The PIMPL state for the Unshadow transform
+/// PIMPL state for the transform
 struct Unshadow::State {
+    /// The source program
+    const Program* const src;
+    /// The target program builder
+    ProgramBuilder b;
     /// The clone context
-    CloneContext& ctx;
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
 
     /// Constructor
-    /// @param context the clone context
-    explicit State(CloneContext& context) : ctx(context) {}
+    /// @param program the source program
+    explicit State(const Program* program) : src(program) {}
 
-    /// Performs the transformation
-    void Run() {
-        auto& sem = ctx.src->Sem();
+    /// Runs the transform
+    /// @returns the new program or SkipTransform if the transform is not required
+    Transform::ApplyResult Run() {
+        auto& sem = src->Sem();
 
         // Maps a variable to its new name.
-        std::unordered_map<const sem::Variable*, Symbol> renamed_to;
+        utils::Hashmap<const sem::Variable*, Symbol, 8> renamed_to;
 
         auto rename = [&](const sem::Variable* v) -> const ast::Variable* {
             auto* decl = v->Declaration();
-            auto name = ctx.src->Symbols().NameFor(decl->symbol);
-            auto symbol = ctx.dst->Symbols().New(name);
-            renamed_to.emplace(v, symbol);
+            auto name = src->Symbols().NameFor(decl->symbol);
+            auto symbol = b.Symbols().New(name);
+            renamed_to.Add(v, symbol);
 
             auto source = ctx.Clone(decl->source);
             auto* type = ctx.Clone(decl->type);
@@ -57,20 +62,20 @@
             return Switch(
                 decl,  //
                 [&](const ast::Var* var) {
-                    return ctx.dst->Var(source, symbol, type, var->declared_address_space,
-                                        var->declared_access, initializer, attributes);
+                    return b.Var(source, symbol, type, var->declared_address_space,
+                                 var->declared_access, initializer, attributes);
                 },
                 [&](const ast::Let*) {
-                    return ctx.dst->Let(source, symbol, type, initializer, attributes);
+                    return b.Let(source, symbol, type, initializer, attributes);
                 },
                 [&](const ast::Const*) {
-                    return ctx.dst->Const(source, symbol, type, initializer, attributes);
+                    return b.Const(source, symbol, type, initializer, attributes);
                 },
-                [&](const ast::Parameter*) {
-                    return ctx.dst->Param(source, symbol, type, attributes);
+                [&](const ast::Parameter*) {  //
+                    return b.Param(source, symbol, type, attributes);
                 },
                 [&](Default) {
-                    TINT_ICE(Transform, ctx.dst->Diagnostics())
+                    TINT_ICE(Transform, b.Diagnostics())
                         << "unexpected variable type: " << decl->TypeInfo().name;
                     return nullptr;
                 });
@@ -92,14 +97,15 @@
         ctx.ReplaceAll(
             [&](const ast::IdentifierExpression* ident) -> const tint::ast::IdentifierExpression* {
                 if (auto* user = sem.Get<sem::VariableUser>(ident)) {
-                    auto it = renamed_to.find(user->Variable());
-                    if (it != renamed_to.end()) {
-                        return ctx.dst->Expr(it->second);
+                    if (auto* renamed = renamed_to.Find(user->Variable())) {
+                        return b.Expr(*renamed);
                     }
                 }
                 return nullptr;
             });
+
         ctx.Clone();
+        return Program(std::move(b));
     }
 };
 
@@ -107,8 +113,8 @@
 
 Unshadow::~Unshadow() = default;
 
-void Unshadow::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    State(ctx).Run();
+Transform::ApplyResult Unshadow::Apply(const Program* src, const DataMap&, DataMap&) const {
+    return State(src).Run();
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/unshadow.h b/src/tint/transform/unshadow.h
index 5ffe839..8ebf105 100644
--- a/src/tint/transform/unshadow.h
+++ b/src/tint/transform/unshadow.h
@@ -29,16 +29,13 @@
     /// Destructor
     ~Unshadow() override;
 
-  protected:
-    struct State;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+  private:
+    struct State;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/unwind_discard_functions.cc b/src/tint/transform/unwind_discard_functions.cc
index 4e20d55..068fe35 100644
--- a/src/tint/transform/unwind_discard_functions.cc
+++ b/src/tint/transform/unwind_discard_functions.cc
@@ -35,7 +35,51 @@
 namespace tint::transform {
 namespace {
 
-class State {
+bool ShouldRun(const Program* program) {
+    auto& sem = program->Sem();
+    for (auto* f : program->AST().Functions()) {
+        if (sem.Get(f)->Behaviors().Contains(sem::Behavior::kDiscard)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace
+
+/// PIMPL state for the transform
+struct UnwindDiscardFunctions::State {
+    /// Constructor
+    /// @param ctx_in the context
+    explicit State(CloneContext& ctx_in) : ctx(ctx_in), b(*ctx_in.dst), sem(ctx_in.src->Sem()) {}
+
+    /// Runs the transform
+    void Run() {
+        ctx.ReplaceAll([&](const ast::BlockStatement* block) -> const ast::Statement* {
+            // Iterate block statements and replace them as needed.
+            for (auto* stmt : block->statements) {
+                if (auto* new_stmt = Statement(stmt)) {
+                    ctx.Replace(stmt, new_stmt);
+                }
+
+                // Handle for loops, as they are the only other AST node that
+                // contains statements outside of BlockStatements.
+                if (auto* fl = stmt->As<ast::ForLoopStatement>()) {
+                    if (auto* new_stmt = Statement(fl->initializer)) {
+                        ctx.Replace(fl->initializer, new_stmt);
+                    }
+                    if (auto* new_stmt = Statement(fl->continuing)) {
+                        // NOTE: Should never reach here as we cannot discard in a
+                        // continuing block.
+                        ctx.Replace(fl->continuing, new_stmt);
+                    }
+                }
+            }
+
+            return nullptr;
+        });
+    }
+
   private:
     CloneContext& ctx;
     ProgramBuilder& b;
@@ -163,7 +207,7 @@
     // Returns true if `stmt` is a for-loop initializer statement.
     bool IsForLoopInitStatement(const ast::Statement* stmt) {
         if (auto* sem_stmt = sem.Get(stmt)) {
-            if (auto* sem_fl = As<sem::ForLoopStatement>(sem_stmt->Parent())) {
+            if (auto* sem_fl = tint::As<sem::ForLoopStatement>(sem_stmt->Parent())) {
                 return sem_fl->Declaration()->initializer == stmt;
             }
         }
@@ -305,60 +349,26 @@
                 return TryInsertAfter(s, sem_expr);
             });
     }
-
-  public:
-    /// Constructor
-    /// @param ctx_in the context
-    explicit State(CloneContext& ctx_in) : ctx(ctx_in), b(*ctx_in.dst), sem(ctx_in.src->Sem()) {}
-
-    /// Runs the transform
-    void Run() {
-        ctx.ReplaceAll([&](const ast::BlockStatement* block) -> const ast::Statement* {
-            // Iterate block statements and replace them as needed.
-            for (auto* stmt : block->statements) {
-                if (auto* new_stmt = Statement(stmt)) {
-                    ctx.Replace(stmt, new_stmt);
-                }
-
-                // Handle for loops, as they are the only other AST node that
-                // contains statements outside of BlockStatements.
-                if (auto* fl = stmt->As<ast::ForLoopStatement>()) {
-                    if (auto* new_stmt = Statement(fl->initializer)) {
-                        ctx.Replace(fl->initializer, new_stmt);
-                    }
-                    if (auto* new_stmt = Statement(fl->continuing)) {
-                        // NOTE: Should never reach here as we cannot discard in a
-                        // continuing block.
-                        ctx.Replace(fl->continuing, new_stmt);
-                    }
-                }
-            }
-
-            return nullptr;
-        });
-
-        ctx.Clone();
-    }
 };
 
-}  // namespace
-
 UnwindDiscardFunctions::UnwindDiscardFunctions() = default;
 UnwindDiscardFunctions::~UnwindDiscardFunctions() = default;
 
-void UnwindDiscardFunctions::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+Transform::ApplyResult UnwindDiscardFunctions::Apply(const Program* src,
+                                                     const DataMap&,
+                                                     DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     State state(ctx);
     state.Run();
-}
 
-bool UnwindDiscardFunctions::ShouldRun(const Program* program, const DataMap& /*data*/) const {
-    auto& sem = program->Sem();
-    for (auto* f : program->AST().Functions()) {
-        if (sem.Get(f)->Behaviors().Contains(sem::Behavior::kDiscard)) {
-            return true;
-        }
-    }
-    return false;
+    ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/unwind_discard_functions.h b/src/tint/transform/unwind_discard_functions.h
index 105a9d8..7614c27 100644
--- a/src/tint/transform/unwind_discard_functions.h
+++ b/src/tint/transform/unwind_discard_functions.h
@@ -44,19 +44,13 @@
     /// Destructor
     ~UnwindDiscardFunctions() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+  private:
+    struct State;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/utils/hoist_to_decl_before.cc b/src/tint/transform/utils/hoist_to_decl_before.cc
index 85bd2b2..e0b35a1 100644
--- a/src/tint/transform/utils/hoist_to_decl_before.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/transform/utils/hoist_to_decl_before.h"
 
-#include <unordered_map>
+#include <utility>
 
 #include "src/tint/program_builder.h"
 #include "src/tint/sem/block_statement.h"
@@ -23,12 +23,66 @@
 #include "src/tint/sem/reference.h"
 #include "src/tint/sem/variable.h"
 #include "src/tint/sem/while_statement.h"
+#include "src/tint/utils/hashmap.h"
 #include "src/tint/utils/reverse.h"
+#include "src/tint/utils/transform.h"
 
 namespace tint::transform {
 
 /// Private implementation of HoistToDeclBefore transform
-class HoistToDeclBefore::State {
+struct HoistToDeclBefore::State {
+    /// Constructor
+    /// @param ctx_in the clone context
+    explicit State(CloneContext& ctx_in) : ctx(ctx_in), b(*ctx_in.dst) {}
+
+    /// @copydoc HoistToDeclBefore::Add()
+    bool Add(const sem::Expression* before_expr,
+             const ast::Expression* expr,
+             bool as_let,
+             const char* decl_name) {
+        auto name = b.Symbols().New(decl_name);
+
+        if (as_let) {
+            auto builder = [this, expr, name] {
+                return b.Decl(b.Let(name, ctx.CloneWithoutTransform(expr)));
+            };
+            if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
+                return false;
+            }
+        } else {
+            auto builder = [this, expr, name] {
+                return b.Decl(b.Var(name, ctx.CloneWithoutTransform(expr)));
+            };
+            if (!InsertBeforeImpl(before_expr->Stmt(), std::move(builder))) {
+                return false;
+            }
+        }
+
+        // Replace the initializer expression with a reference to the let
+        ctx.Replace(expr, b.Expr(name));
+        return true;
+    }
+
+    /// @copydoc HoistToDeclBefore::InsertBefore(const sem::Statement*, const ast::Statement*)
+    bool InsertBefore(const sem::Statement* before_stmt, const ast::Statement* stmt) {
+        if (stmt) {
+            auto builder = [stmt] { return stmt; };
+            return InsertBeforeImpl(before_stmt, std::move(builder));
+        }
+        return InsertBeforeImpl(before_stmt, Decompose{});
+    }
+
+    /// @copydoc HoistToDeclBefore::InsertBefore(const sem::Statement*, const StmtBuilder&)
+    bool InsertBefore(const sem::Statement* before_stmt, const StmtBuilder& builder) {
+        return InsertBeforeImpl(before_stmt, std::move(builder));
+    }
+
+    /// @copydoc HoistToDeclBefore::Prepare()
+    bool Prepare(const sem::Expression* before_expr) {
+        return InsertBefore(before_expr->Stmt(), nullptr);
+    }
+
+  private:
     CloneContext& ctx;
     ProgramBuilder& b;
 
@@ -36,46 +90,78 @@
     /// loop, so that declaration statements can be inserted before the
     /// condition expression or continuing statement.
     struct LoopInfo {
-        utils::Vector<const ast::Statement*, 8> cond_decls;
-        utils::Vector<const ast::Statement*, 8> cont_decls;
+        utils::Vector<StmtBuilder, 8> cond_decls;
+        utils::Vector<StmtBuilder, 8> cont_decls;
     };
 
     /// Info for each else-if that needs decomposing
     struct ElseIfInfo {
         /// Decls to insert before condition
-        utils::Vector<const ast::Statement*, 8> cond_decls;
+        utils::Vector<StmtBuilder, 8> cond_decls;
     };
 
     /// For-loops that need to be decomposed to loops.
-    std::unordered_map<const sem::ForLoopStatement*, LoopInfo> for_loops;
+    utils::Hashmap<const sem::ForLoopStatement*, LoopInfo, 4> for_loops;
 
     /// Whiles that need to be decomposed to loops.
-    std::unordered_map<const sem::WhileStatement*, LoopInfo> while_loops;
+    utils::Hashmap<const sem::WhileStatement*, LoopInfo, 4> while_loops;
 
     /// 'else if' statements that need to be decomposed to 'else {if}'
-    std::unordered_map<const ast::IfStatement*, ElseIfInfo> else_ifs;
+    utils::Hashmap<const ast::IfStatement*, ElseIfInfo, 4> else_ifs;
 
-    // Converts any for-loops marked for conversion to loops, inserting
-    // registered declaration statements before the condition or continuing
-    // statement.
-    void ForLoopsToLoops() {
-        if (for_loops.empty()) {
-            return;
+    template <size_t N>
+    static auto Build(const utils::Vector<StmtBuilder, N>& builders) {
+        return utils::Transform(builders, [&](auto& builder) { return builder(); });
+    }
+
+    /// @returns a new LoopInfo reference for the given @p for_loop.
+    /// @note if this is the first call to this method, then RegisterForLoopTransform() is
+    /// automatically called.
+    /// @warning the returned reference is invalid if this is called a second time, or the
+    /// #for_loops map is mutated.
+    LoopInfo& ForLoop(const sem::ForLoopStatement* for_loop) {
+        if (for_loops.IsEmpty()) {
+            RegisterForLoopTransform();
         }
+        return for_loops.GetOrZero(for_loop);
+    }
 
-        // At least one for-loop needs to be transformed into a loop.
+    /// @returns a new LoopInfo reference for the given @p while_loop.
+    /// @note if this is the first call to this method, then RegisterWhileLoopTransform() is
+    /// automatically called.
+    /// @warning the returned reference is invalid if this is called a second time, or the
+    /// #for_loops map is mutated.
+    LoopInfo& WhileLoop(const sem::WhileStatement* while_loop) {
+        if (while_loops.IsEmpty()) {
+            RegisterWhileLoopTransform();
+        }
+        return while_loops.GetOrZero(while_loop);
+    }
+
+    /// @returns a new ElseIfInfo reference for the given @p else_if.
+    /// @note if this is the first call to this method, then RegisterElseIfTransform() is
+    /// automatically called.
+    /// @warning the returned reference is invalid if this is called a second time, or the
+    /// #else_ifs map is mutated.
+    ElseIfInfo& ElseIf(const ast::IfStatement* else_if) {
+        if (else_ifs.IsEmpty()) {
+            RegisterElseIfTransform();
+        }
+        return else_ifs.GetOrZero(else_if);
+    }
+
+    /// Registers the handler for transforming for-loops based on the content of the #for_loops map.
+    void RegisterForLoopTransform() const {
         ctx.ReplaceAll([&](const ast::ForLoopStatement* stmt) -> const ast::Statement* {
             auto& sem = ctx.src->Sem();
 
             if (auto* fl = sem.Get(stmt)) {
-                if (auto it = for_loops.find(fl); it != for_loops.end()) {
-                    auto& info = it->second;
+                if (auto* info = for_loops.Find(fl)) {
                     auto* for_loop = fl->Declaration();
                     // For-loop needs to be decomposed to a loop.
                     // Build the loop body's statements.
-                    // Start with any let declarations for the conditional
-                    // expression.
-                    auto body_stmts = info.cond_decls;
+                    // Start with any let declarations for the conditional expression.
+                    auto body_stmts = Build(info->cond_decls);
                     // If the for-loop has a condition, emit this next as:
                     //   if (!cond) { break; }
                     if (auto* cond = for_loop->condition) {
@@ -95,7 +181,7 @@
                     if (auto* cont = for_loop->continuing) {
                         // Continuing block starts with any let declarations used by
                         // the continuing.
-                        auto cont_stmts = info.cont_decls;
+                        auto cont_stmts = Build(info->cont_decls);
                         cont_stmts.Push(ctx.Clone(cont));
                         continuing = b.Block(cont_stmts);
                     }
@@ -112,34 +198,29 @@
         });
     }
 
-    // Converts any while-loops marked for conversion to loops, inserting
-    // registered declaration statements before the condition.
-    void WhilesToLoops() {
-        if (while_loops.empty()) {
-            return;
-        }
-
+    /// Registers the handler for transforming while-loops based on the content of the #while_loops
+    /// map.
+    void RegisterWhileLoopTransform() const {
         // At least one while needs to be transformed into a loop.
         ctx.ReplaceAll([&](const ast::WhileStatement* stmt) -> const ast::Statement* {
             auto& sem = ctx.src->Sem();
 
             if (auto* w = sem.Get(stmt)) {
-                if (auto it = while_loops.find(w); it != while_loops.end()) {
-                    auto& info = it->second;
+                if (auto* info = while_loops.Find(w)) {
                     auto* while_loop = w->Declaration();
                     // While needs to be decomposed to a loop.
                     // Build the loop body's statements.
                     // Start with any let declarations for the conditional
                     // expression.
-                    auto body_stmts = info.cond_decls;
+                    auto body_stmts = utils::Transform(info->cond_decls,
+                                                       [&](auto& builder) { return builder(); });
                     // Emit the condition as:
                     //   if (!cond) { break; }
                     auto* cond = while_loop->condition;
                     // !condition
-                    auto* not_cond =
-                        b.create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, ctx.Clone(cond));
+                    auto* not_cond = b.Not(ctx.Clone(cond));
                     // { break; }
-                    auto* break_body = b.Block(b.create<ast::BreakStatement>());
+                    auto* break_body = b.Block(b.Break());
                     // if (!condition) { break; }
                     body_stmts.Push(b.If(not_cond, break_body));
 
@@ -157,81 +238,49 @@
         });
     }
 
-    void ElseIfsToElseWithNestedIfs() {
+    /// Registers the handler for transforming if-statements based on the content of the #else_ifs
+    /// map.
+    void RegisterElseIfTransform() const {
         // Decompose 'else-if' statements into 'else { if }' blocks.
-        ctx.ReplaceAll([&](const ast::IfStatement* else_if) -> const ast::Statement* {
-            if (!else_ifs.count(else_if)) {
-                return nullptr;
+        ctx.ReplaceAll([&](const ast::IfStatement* stmt) -> const ast::Statement* {
+            if (auto* info = else_ifs.Find(stmt)) {
+                // Build the else block's body statements, starting with let decls for the
+                // conditional expression.
+                auto body_stmts = Build(info->cond_decls);
+
+                // Move the 'else-if' into the new `else` block as a plain 'if'.
+                auto* cond = ctx.Clone(stmt->condition);
+                auto* body = ctx.Clone(stmt->body);
+                auto* new_if = b.If(cond, body, b.Else(ctx.Clone(stmt->else_statement)));
+                body_stmts.Push(new_if);
+
+                // Replace the 'else-if' with the new 'else' block.
+                return b.Block(body_stmts);
             }
-            auto& else_if_info = else_ifs[else_if];
-
-            // Build the else block's body statements, starting with let decls for
-            // the conditional expression.
-            auto& body_stmts = else_if_info.cond_decls;
-
-            // Move the 'else-if' into the new `else` block as a plain 'if'.
-            auto* cond = ctx.Clone(else_if->condition);
-            auto* body = ctx.Clone(else_if->body);
-            auto* new_if = b.If(cond, body, b.Else(ctx.Clone(else_if->else_statement)));
-            body_stmts.Push(new_if);
-
-            // Replace the 'else-if' with the new 'else' block.
-            return b.Block(body_stmts);
+            return nullptr;
         });
     }
 
-  public:
-    /// Constructor
-    /// @param ctx_in the clone context
-    explicit State(CloneContext& ctx_in) : ctx(ctx_in), b(*ctx_in.dst) {}
+    /// A type used to signal to InsertBeforeImpl that no insertion should take place - instead flow
+    /// control statements should just be decomposed.
+    struct Decompose {};
 
-    /// Hoists `expr` to a `let` or `var` with optional `decl_name`, inserting it
-    /// before `before_expr`.
-    /// @param before_expr expression to insert `expr` before
-    /// @param expr expression to hoist
-    /// @param as_let hoist to `let` if true, otherwise to `var`
-    /// @param decl_name optional name to use for the variable/constant name
-    /// @return true on success
-    bool Add(const sem::Expression* before_expr,
-             const ast::Expression* expr,
-             bool as_let,
-             const char* decl_name) {
-        auto name = b.Symbols().New(decl_name);
+    template <typename BUILDER>
+    bool InsertBeforeImpl(const sem::Statement* before_stmt, BUILDER&& builder) {
+        (void)builder;  // Avoid 'unused parameter' warning due to 'if constexpr'
 
-        // Construct the let/var that holds the hoisted expr
-        auto* v = as_let ? static_cast<const ast::Variable*>(b.Let(name, ctx.Clone(expr)))
-                         : static_cast<const ast::Variable*>(b.Var(name, ctx.Clone(expr)));
-        auto* decl = b.Decl(v);
-
-        if (!InsertBefore(before_expr->Stmt(), decl)) {
-            return false;
-        }
-
-        // Replace the initializer expression with a reference to the let
-        ctx.Replace(expr, b.Expr(name));
-        return true;
-    }
-
-    /// Inserts `stmt` before `before_stmt`, possibly marking a for-loop to be
-    /// converted to a loop, or an else-if to an else { if }. If `decl` is
-    /// nullptr, for-loop and else-if conversions are marked, but no hoisting
-    /// takes place.
-    /// @param before_stmt statement to insert `stmt` before
-    /// @param stmt statement to insert
-    /// @return true on success
-    bool InsertBefore(const sem::Statement* before_stmt, const ast::Statement* stmt) {
         auto* ip = before_stmt->Declaration();
 
         auto* else_if = before_stmt->As<sem::IfStatement>();
         if (else_if && else_if->Parent()->Is<sem::IfStatement>()) {
             // Insertion point is an 'else if' condition.
             // Need to convert 'else if' to 'else { if }'.
-            auto& else_if_info = else_ifs[else_if->Declaration()];
+            auto& else_if_info = ElseIf(else_if->Declaration());
 
             // Index the map to convert this else if, even if `stmt` is nullptr.
             auto& decls = else_if_info.cond_decls;
-            if (stmt) {
-                decls.Push(stmt);
+            if constexpr (!std::is_same_v<BUILDER, Decompose>) {
+                decls.Push(std::forward<BUILDER>(builder));
             }
             return true;
         }
@@ -241,9 +290,9 @@
             // For-loop needs to be decomposed to a loop.
 
             // Index the map to convert this for-loop, even if `stmt` is nullptr.
-            auto& decls = for_loops[fl].cond_decls;
-            if (stmt) {
-                decls.Push(stmt);
+            auto& decls = ForLoop(fl).cond_decls;
+            if constexpr (!std::is_same_v<BUILDER, Decompose>) {
+                decls.Push(std::forward<BUILDER>(builder));
             }
             return true;
         }
@@ -253,9 +302,9 @@
             // While needs to be decomposed to a loop.
 
             // Index the map to convert this while, even if `stmt` is nullptr.
-            auto& decls = while_loops[w].cond_decls;
-            if (stmt) {
-                decls.Push(stmt);
+            auto& decls = WhileLoop(w).cond_decls;
+            if constexpr (!std::is_same_v<BUILDER, Decompose>) {
+                decls.Push(std::forward<BUILDER>(builder));
             }
             return true;
         }
@@ -264,8 +313,9 @@
         if (auto* block = parent->As<sem::BlockStatement>()) {
             // Insert point sits in a block. Simple case.
             // Insert the stmt before the parent statement.
-            if (stmt) {
-                ctx.InsertBefore(block->Declaration()->statements, ip, stmt);
+            if constexpr (!std::is_same_v<BUILDER, Decompose>) {
+                ctx.InsertBefore(block->Declaration()->statements, ip,
+                                 std::forward<BUILDER>(builder));
             }
             return true;
         }
@@ -276,9 +326,9 @@
             if (fl->Declaration()->initializer == ip) {
                 // Insertion point is a for-loop initializer.
                 // Insert the new statement above the for-loop.
-                if (stmt) {
+                if constexpr (!std::is_same_v<BUILDER, Decompose>) {
                     ctx.InsertBefore(fl->Block()->Declaration()->statements, fl->Declaration(),
-                                     stmt);
+                                     std::forward<BUILDER>(builder));
                 }
                 return true;
             }
@@ -288,9 +338,9 @@
                 // For-loop needs to be decomposed to a loop.
 
                 // Index the map to convert this for-loop, even if `stmt` is nullptr.
-                auto& decls = for_loops[fl].cont_decls;
-                if (stmt) {
-                    decls.Push(stmt);
+                auto& decls = ForLoop(fl).cont_decls;
+                if constexpr (!std::is_same_v<BUILDER, Decompose>) {
+                    decls.Push(std::forward<BUILDER>(builder));
                 }
                 return true;
             }
@@ -303,25 +353,6 @@
             << "unhandled expression parent statement type: " << parent->TypeInfo().name;
         return false;
     }
-
-    /// Use to signal that we plan on hoisting a decl before `before_expr`. This
-    /// will convert 'for-loop's to 'loop's and 'else-if's to 'else {if}'s if
-    /// needed.
-    /// @param before_expr expression we would hoist a decl before
-    /// @return true on success
-    bool Prepare(const sem::Expression* before_expr) {
-        return InsertBefore(before_expr->Stmt(), nullptr);
-    }
-
-    /// Applies any scheduled insertions from previous calls to Add() to
-    /// CloneContext. Call this once before ctx.Clone().
-    /// @return true on success
-    bool Apply() {
-        ForLoopsToLoops();
-        WhilesToLoops();
-        ElseIfsToElseWithNestedIfs();
-        return true;
-    }
 };
 
 HoistToDeclBefore::HoistToDeclBefore(CloneContext& ctx) : state_(std::make_unique<State>(ctx)) {}
@@ -340,12 +371,13 @@
     return state_->InsertBefore(before_stmt, stmt);
 }
 
-bool HoistToDeclBefore::Prepare(const sem::Expression* before_expr) {
-    return state_->Prepare(before_expr);
+bool HoistToDeclBefore::InsertBefore(const sem::Statement* before_stmt,
+                                     const StmtBuilder& builder) {
+    return state_->InsertBefore(before_stmt, builder);
 }
 
-bool HoistToDeclBefore::Apply() {
-    return state_->Apply();
+bool HoistToDeclBefore::Prepare(const sem::Expression* before_expr) {
+    return state_->Prepare(before_expr);
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/utils/hoist_to_decl_before.h b/src/tint/transform/utils/hoist_to_decl_before.h
index d0b96e0..d9a8a8a 100644
--- a/src/tint/transform/utils/hoist_to_decl_before.h
+++ b/src/tint/transform/utils/hoist_to_decl_before.h
@@ -15,6 +15,7 @@
 #ifndef SRC_TINT_TRANSFORM_UTILS_HOIST_TO_DECL_BEFORE_H_
 #define SRC_TINT_TRANSFORM_UTILS_HOIST_TO_DECL_BEFORE_H_
 
+#include <functional>
 #include <memory>
 
 #include "src/tint/sem/expression.h"
@@ -34,25 +35,40 @@
     /// Destructor
     ~HoistToDeclBefore();
 
-    /// Hoists `expr` to a `let` or `var` with optional `decl_name`, inserting it
-    /// before `before_expr`.
+    /// StmtBuilder is a builder of an AST statement
+    using StmtBuilder = std::function<const ast::Statement*()>;
+
+    /// Hoists @p expr to a `let` or `var` with optional `decl_name`, inserting it
+    /// before @p before_expr.
     /// @param before_expr expression to insert `expr` before
     /// @param expr expression to hoist
-    /// @param as_const hoist to `let` if true, otherwise to `var`
+    /// @param as_let hoist to `let` if true, otherwise to `var`
     /// @param decl_name optional name to use for the variable/constant name
     /// @return true on success
     bool Add(const sem::Expression* before_expr,
              const ast::Expression* expr,
-             bool as_const,
+             bool as_let,
              const char* decl_name = "");
 
-    /// Inserts `stmt` before `before_stmt`, possibly converting 'for-loop's to
-    /// 'loop's if necessary.
-    /// @param before_stmt statement to insert `stmt` before
+    /// Inserts @p stmt before @p before_stmt, possibly converting 'for-loop's to 'loop's if
+    /// necessary.
+    /// @warning If the container of @p before_stmt is cloned multiple times, then the resolver will
+    /// ICE as the same statement cannot be shared.
+    /// @param before_stmt statement to insert @p stmt before
     /// @param stmt statement to insert
     /// @return true on success
     bool InsertBefore(const sem::Statement* before_stmt, const ast::Statement* stmt);
 
+    /// Inserts the returned statement of @p builder before @p before_stmt, possibly converting
+    /// 'for-loop's to 'loop's if necessary.
+    /// @note If the container of @p before_stmt is cloned multiple times, then @p builder will be
+    /// called for each clone.
+    /// @param before_stmt the preceding statement that the statement of @p builder will be inserted
+    /// before
+    /// @param builder the statement builder used to create the new statement
+    /// @return true on success
+    bool InsertBefore(const sem::Statement* before_stmt, const StmtBuilder& builder);
+
     /// Use to signal that we plan on hoisting a decl before `before_expr`. This
     /// will convert 'for-loop's to 'loop's and 'else-if's to 'else {if}'s if
     /// needed.
@@ -60,13 +76,8 @@
     /// @return true on success
     bool Prepare(const sem::Expression* before_expr);
 
-    /// Applies any scheduled insertions from previous calls to Add() to
-    /// CloneContext. Call this once before ctx.Clone().
-    /// @return true on success
-    bool Apply();
-
   private:
-    class State;
+    struct State;
     std::unique_ptr<State> state_;
 };
 
diff --git a/src/tint/transform/utils/hoist_to_decl_before_test.cc b/src/tint/transform/utils/hoist_to_decl_before_test.cc
index f0d3b36..7b45e01 100644
--- a/src/tint/transform/utils/hoist_to_decl_before_test.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before_test.cc
@@ -45,7 +45,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Add(sem_expr, expr, true);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -77,7 +76,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Add(sem_expr, expr, true);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -112,7 +110,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Add(sem_expr, expr, true);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -151,7 +148,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Add(sem_expr, expr, true);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -195,7 +191,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Add(sem_expr, expr, true);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -240,7 +235,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Add(sem_expr, expr, true);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -279,7 +273,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Add(sem_expr, expr, true);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -314,7 +307,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Add(sem_expr, expr, true);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -349,7 +341,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Prepare(sem_expr);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -387,7 +378,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Prepare(sem_expr);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -434,7 +424,6 @@
     HoistToDeclBefore hoistToDeclBefore(ctx);
     auto* sem_expr = ctx.src->Sem().Get(expr);
     hoistToDeclBefore.Prepare(sem_expr);
-    hoistToDeclBefore.Apply();
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -473,7 +462,42 @@
     auto* before_stmt = ctx.src->Sem().Get(var);
     auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
     hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-    hoistToDeclBefore.Apply();
+
+    ctx.Clone();
+    Program cloned(std::move(cloned_b));
+
+    auto* expect = R"(
+fn foo() {
+}
+
+fn f() {
+  foo();
+  var a = 1i;
+}
+)";
+
+    EXPECT_EQ(expect, str(cloned));
+}
+
+TEST_F(HoistToDeclBeforeTest, InsertBefore_Block_Function) {
+    // fn foo() {
+    // }
+    // fn f() {
+    //     var a = 1i;
+    // }
+    ProgramBuilder b;
+    b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
+    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
+    b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{var});
+
+    Program original(std::move(b));
+    ProgramBuilder cloned_b;
+    CloneContext ctx(&cloned_b, &original);
+
+    HoistToDeclBefore hoistToDeclBefore(ctx);
+    auto* before_stmt = ctx.src->Sem().Get(var);
+    hoistToDeclBefore.InsertBefore(before_stmt,
+                                   [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -512,7 +536,45 @@
     auto* before_stmt = ctx.src->Sem().Get(var);
     auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
     hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-    hoistToDeclBefore.Apply();
+
+    ctx.Clone();
+    Program cloned(std::move(cloned_b));
+
+    auto* expect = R"(
+fn foo() {
+}
+
+fn f() {
+  foo();
+  for(var a = 1i; true; ) {
+  }
+}
+)";
+
+    EXPECT_EQ(expect, str(cloned));
+}
+
+TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopInit_Function) {
+    // fn foo() {
+    // }
+    // fn f() {
+    //     for(var a = 1i; true;) {
+    //     }
+    // }
+    ProgramBuilder b;
+    b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
+    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
+    auto* s = b.For(var, b.Expr(true), nullptr, b.Block());
+    b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{s});
+
+    Program original(std::move(b));
+    ProgramBuilder cloned_b;
+    CloneContext ctx(&cloned_b, &original);
+
+    HoistToDeclBefore hoistToDeclBefore(ctx);
+    auto* before_stmt = ctx.src->Sem().Get(var);
+    hoistToDeclBefore.InsertBefore(before_stmt,
+                                   [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -554,7 +616,57 @@
     auto* before_stmt = ctx.src->Sem().Get(cont->As<ast::Statement>());
     auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
     hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-    hoistToDeclBefore.Apply();
+
+    ctx.Clone();
+    Program cloned(std::move(cloned_b));
+
+    auto* expect = R"(
+fn foo() {
+}
+
+fn f() {
+  var a = 1i;
+  loop {
+    if (!(true)) {
+      break;
+    }
+    {
+    }
+
+    continuing {
+      foo();
+      a += 1i;
+    }
+  }
+}
+)";
+
+    EXPECT_EQ(expect, str(cloned));
+}
+
+TEST_F(HoistToDeclBeforeTest, InsertBefore_ForLoopCont_Function) {
+    // fn foo() {
+    // }
+    // fn f() {
+    //     var a = 1i;
+    //     for(; true; a+=1i) {
+    //     }
+    // }
+    ProgramBuilder b;
+    b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
+    auto* var = b.Decl(b.Var("a", b.Expr(1_i)));
+    auto* cont = b.CompoundAssign("a", b.Expr(1_i), ast::BinaryOp::kAdd);
+    auto* s = b.For(nullptr, b.Expr(true), cont, b.Block());
+    b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{var, s});
+
+    Program original(std::move(b));
+    ProgramBuilder cloned_b;
+    CloneContext ctx(&cloned_b, &original);
+
+    HoistToDeclBefore hoistToDeclBefore(ctx);
+    auto* before_stmt = ctx.src->Sem().Get(cont->As<ast::Statement>());
+    hoistToDeclBefore.InsertBefore(before_stmt,
+                                   [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
@@ -609,7 +721,55 @@
     auto* before_stmt = ctx.src->Sem().Get(elseif);
     auto* new_stmt = ctx.dst->CallStmt(ctx.dst->Call("foo"));
     hoistToDeclBefore.InsertBefore(before_stmt, new_stmt);
-    hoistToDeclBefore.Apply();
+
+    ctx.Clone();
+    Program cloned(std::move(cloned_b));
+
+    auto* expect = R"(
+fn foo() {
+}
+
+fn f() {
+  var a : bool;
+  if (true) {
+  } else {
+    foo();
+    if (a) {
+    } else {
+    }
+  }
+}
+)";
+
+    EXPECT_EQ(expect, str(cloned));
+}
+
+TEST_F(HoistToDeclBeforeTest, InsertBefore_ElseIf_Function) {
+    // fn foo() {
+    // }
+    // fn f() {
+    //     var a : bool;
+    //     if (true) {
+    //     } else if (a) {
+    //     } else {
+    //     }
+    // }
+    ProgramBuilder b;
+    b.Func("foo", utils::Empty, b.ty.void_(), utils::Empty);
+    auto* var = b.Decl(b.Var("a", b.ty.bool_()));
+    auto* elseif = b.If(b.Expr("a"), b.Block(), b.Else(b.Block()));
+    auto* s = b.If(b.Expr(true), b.Block(),  //
+                   b.Else(elseif));
+    b.Func("f", utils::Empty, b.ty.void_(), utils::Vector{var, s});
+
+    Program original(std::move(b));
+    ProgramBuilder cloned_b;
+    CloneContext ctx(&cloned_b, &original);
+
+    HoistToDeclBefore hoistToDeclBefore(ctx);
+    auto* before_stmt = ctx.src->Sem().Get(elseif);
+    hoistToDeclBefore.InsertBefore(before_stmt,
+                                   [&] { return ctx.dst->CallStmt(ctx.dst->Call("foo")); });
 
     ctx.Clone();
     Program cloned(std::move(cloned_b));
diff --git a/src/tint/transform/var_for_dynamic_index.cc b/src/tint/transform/var_for_dynamic_index.cc
index aaebdc7..81af013 100644
--- a/src/tint/transform/var_for_dynamic_index.cc
+++ b/src/tint/transform/var_for_dynamic_index.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #include "src/tint/transform/var_for_dynamic_index.h"
+
+#include <utility>
+
 #include "src/tint/program_builder.h"
 #include "src/tint/transform/utils/hoist_to_decl_before.h"
 
@@ -22,7 +25,12 @@
 
 VarForDynamicIndex::~VarForDynamicIndex() = default;
 
-void VarForDynamicIndex::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+Transform::ApplyResult VarForDynamicIndex::Apply(const Program* src,
+                                                 const DataMap&,
+                                                 DataMap&) const {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     HoistToDeclBefore hoist_to_decl_before(ctx);
 
     // Extracts array and matrix values that are dynamically indexed to a
@@ -30,7 +38,7 @@
     auto dynamic_index_to_var = [&](const ast::IndexAccessorExpression* access_expr) {
         auto* index_expr = access_expr->index;
         auto* object_expr = access_expr->object;
-        auto& sem = ctx.src->Sem();
+        auto& sem = src->Sem();
 
         if (sem.Get(index_expr)->ConstantValue()) {
             // Index expression resolves to a compile time value.
@@ -49,16 +57,21 @@
         return hoist_to_decl_before.Add(indexed, object_expr, false, "var_for_index");
     };
 
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
+    bool index_accessor_found = false;
+    for (auto* node : src->ASTNodes().Objects()) {
         if (auto* access_expr = node->As<ast::IndexAccessorExpression>()) {
             if (!dynamic_index_to_var(access_expr)) {
-                return;
+                return Program(std::move(b));
             }
+            index_accessor_found = true;
         }
     }
+    if (!index_accessor_found) {
+        return SkipTransform;
+    }
 
-    hoist_to_decl_before.Apply();
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/var_for_dynamic_index.h b/src/tint/transform/var_for_dynamic_index.h
index 39ef2f2..070a2cd 100644
--- a/src/tint/transform/var_for_dynamic_index.h
+++ b/src/tint/transform/var_for_dynamic_index.h
@@ -31,14 +31,10 @@
     /// Destructor
     ~VarForDynamicIndex() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/vectorize_matrix_conversions.cc b/src/tint/transform/vectorize_matrix_conversions.cc
index 576b885..94fbdf3 100644
--- a/src/tint/transform/vectorize_matrix_conversions.cc
+++ b/src/tint/transform/vectorize_matrix_conversions.cc
@@ -30,11 +30,9 @@
 
 namespace tint::transform {
 
-VectorizeMatrixConversions::VectorizeMatrixConversions() = default;
+namespace {
 
-VectorizeMatrixConversions::~VectorizeMatrixConversions() = default;
-
-bool VectorizeMatrixConversions::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     for (auto* node : program->ASTNodes().Objects()) {
         if (auto* sem = program->Sem().Get<sem::Expression>(node)) {
             if (auto* call = sem->UnwrapMaterialize()->As<sem::Call>()) {
@@ -50,14 +48,29 @@
     return false;
 }
 
-void VectorizeMatrixConversions::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+}  // namespace
+
+VectorizeMatrixConversions::VectorizeMatrixConversions() = default;
+
+VectorizeMatrixConversions::~VectorizeMatrixConversions() = default;
+
+Transform::ApplyResult VectorizeMatrixConversions::Apply(const Program* src,
+                                                         const DataMap&,
+                                                         DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     using HelperFunctionKey =
         utils::UnorderedKeyWrapper<std::tuple<const sem::Matrix*, const sem::Matrix*>>;
 
     std::unordered_map<HelperFunctionKey, Symbol> matrix_convs;
 
     ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::CallExpression* {
-        auto* call = ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
+        auto* call = src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
         auto* ty_conv = call->Target()->As<sem::TypeConversion>();
         if (!ty_conv) {
             return nullptr;
@@ -72,16 +85,16 @@
             return nullptr;
         }
 
-        auto& src = args[0];
+        auto& matrix = args[0];
 
-        auto* src_type = args[0]->Type()->UnwrapRef()->As<sem::Matrix>();
+        auto* src_type = matrix->Type()->UnwrapRef()->As<sem::Matrix>();
         if (!src_type) {
             return nullptr;
         }
 
         // The source and destination type of a matrix conversion must have a same shape.
         if (!(src_type->rows() == dst_type->rows() && src_type->columns() == dst_type->columns())) {
-            TINT_ICE(Transform, ctx.dst->Diagnostics())
+            TINT_ICE(Transform, b.Diagnostics())
                 << "source and destination matrix has different shape in matrix conversion";
             return nullptr;
         }
@@ -90,47 +103,45 @@
             utils::Vector<const ast::Expression*, 4> columns;
             for (uint32_t c = 0; c < dst_type->columns(); c++) {
                 auto* src_matrix_expr = src_expression_builder();
-                auto* src_column_expr =
-                    ctx.dst->IndexAccessor(src_matrix_expr, ctx.dst->Expr(tint::AInt(c)));
-                columns.Push(ctx.dst->Construct(CreateASTTypeFor(ctx, dst_type->ColumnType()),
-                                                src_column_expr));
+                auto* src_column_expr = b.IndexAccessor(src_matrix_expr, b.Expr(tint::AInt(c)));
+                columns.Push(
+                    b.Construct(CreateASTTypeFor(ctx, dst_type->ColumnType()), src_column_expr));
             }
-            return ctx.dst->Construct(CreateASTTypeFor(ctx, dst_type), columns);
+            return b.Construct(CreateASTTypeFor(ctx, dst_type), columns);
         };
 
         // Replace the matrix conversion to column vector conversions and a matrix construction.
-        if (!src->HasSideEffects()) {
+        if (!matrix->HasSideEffects()) {
             // Simply use the argument's declaration if it has no side effects.
             return build_vectorized_conversion_expression([&]() {  //
-                return ctx.Clone(src->Declaration());
+                return ctx.Clone(matrix->Declaration());
             });
         } else {
             // If has side effects, use a helper function.
             auto fn =
                 utils::GetOrCreate(matrix_convs, HelperFunctionKey{{src_type, dst_type}}, [&] {
-                    auto name =
-                        ctx.dst->Symbols().New("convert_mat" + std::to_string(src_type->columns()) +
-                                               "x" + std::to_string(src_type->rows()) + "_" +
-                                               ctx.dst->FriendlyName(src_type->type()) + "_" +
-                                               ctx.dst->FriendlyName(dst_type->type()));
-                    ctx.dst->Func(
-                        name,
-                        utils::Vector{
-                            ctx.dst->Param("value", CreateASTTypeFor(ctx, src_type)),
-                        },
-                        CreateASTTypeFor(ctx, dst_type),
-                        utils::Vector{
-                            ctx.dst->Return(build_vectorized_conversion_expression([&]() {  //
-                                return ctx.dst->Expr("value");
-                            })),
-                        });
+                    auto name = b.Symbols().New(
+                        "convert_mat" + std::to_string(src_type->columns()) + "x" +
+                        std::to_string(src_type->rows()) + "_" + b.FriendlyName(src_type->type()) +
+                        "_" + b.FriendlyName(dst_type->type()));
+                    b.Func(name,
+                           utils::Vector{
+                               b.Param("value", CreateASTTypeFor(ctx, src_type)),
+                           },
+                           CreateASTTypeFor(ctx, dst_type),
+                           utils::Vector{
+                               b.Return(build_vectorized_conversion_expression([&]() {  //
+                                   return b.Expr("value");
+                               })),
+                           });
                     return name;
                 });
-            return ctx.dst->Call(fn, ctx.Clone(args[0]->Declaration()));
+            return b.Call(fn, ctx.Clone(args[0]->Declaration()));
         }
     });
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/vectorize_matrix_conversions.h b/src/tint/transform/vectorize_matrix_conversions.h
index f16467c..c86240c 100644
--- a/src/tint/transform/vectorize_matrix_conversions.h
+++ b/src/tint/transform/vectorize_matrix_conversions.h
@@ -28,19 +28,10 @@
     /// Destructor
     ~VectorizeMatrixConversions() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/vectorize_scalar_matrix_initializers.cc b/src/tint/transform/vectorize_scalar_matrix_initializers.cc
index 97b0e4f..e6e1c46 100644
--- a/src/tint/transform/vectorize_scalar_matrix_initializers.cc
+++ b/src/tint/transform/vectorize_scalar_matrix_initializers.cc
@@ -27,12 +27,9 @@
 TINT_INSTANTIATE_TYPEINFO(tint::transform::VectorizeScalarMatrixInitializers);
 
 namespace tint::transform {
+namespace {
 
-VectorizeScalarMatrixInitializers::VectorizeScalarMatrixInitializers() = default;
-
-VectorizeScalarMatrixInitializers::~VectorizeScalarMatrixInitializers() = default;
-
-bool VectorizeScalarMatrixInitializers::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     for (auto* node : program->ASTNodes().Objects()) {
         if (auto* call = program->Sem().Get<sem::Call>(node)) {
             if (call->Target()->Is<sem::TypeInitializer>() && call->Type()->Is<sem::Matrix>()) {
@@ -46,11 +43,26 @@
     return false;
 }
 
-void VectorizeScalarMatrixInitializers::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+}  // namespace
+
+VectorizeScalarMatrixInitializers::VectorizeScalarMatrixInitializers() = default;
+
+VectorizeScalarMatrixInitializers::~VectorizeScalarMatrixInitializers() = default;
+
+Transform::ApplyResult VectorizeScalarMatrixInitializers::Apply(const Program* src,
+                                                                const DataMap&,
+                                                                DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     std::unordered_map<const sem::Matrix*, Symbol> scalar_inits;
 
     ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::CallExpression* {
-        auto* call = ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
+        auto* call = src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
         auto* ty_init = call->Target()->As<sem::TypeInitializer>();
         if (!ty_init) {
             return nullptr;
@@ -87,10 +99,10 @@
                 }
 
                 // Construct the column vector.
-                columns.Push(ctx.dst->vec(CreateASTTypeFor(ctx, mat_type->type()), mat_type->rows(),
-                                          std::move(row_values)));
+                columns.Push(b.vec(CreateASTTypeFor(ctx, mat_type->type()), mat_type->rows(),
+                                   std::move(row_values)));
             }
-            return ctx.dst->Construct(CreateASTTypeFor(ctx, mat_type), columns);
+            return b.Construct(CreateASTTypeFor(ctx, mat_type), columns);
         };
 
         if (args.Length() == 1) {
@@ -98,23 +110,22 @@
             // This is done to ensure that the single argument value is only evaluated once, and
             // with the correct expression evaluation order.
             auto fn = utils::GetOrCreate(scalar_inits, mat_type, [&] {
-                auto name =
-                    ctx.dst->Symbols().New("build_mat" + std::to_string(mat_type->columns()) + "x" +
-                                           std::to_string(mat_type->rows()));
-                ctx.dst->Func(name,
-                              utils::Vector{
-                                  // Single scalar parameter
-                                  ctx.dst->Param("value", CreateASTTypeFor(ctx, mat_type->type())),
-                              },
-                              CreateASTTypeFor(ctx, mat_type),
-                              utils::Vector{
-                                  ctx.dst->Return(build_mat([&](uint32_t, uint32_t) {  //
-                                      return ctx.dst->Expr("value");
-                                  })),
-                              });
+                auto name = b.Symbols().New("build_mat" + std::to_string(mat_type->columns()) +
+                                            "x" + std::to_string(mat_type->rows()));
+                b.Func(name,
+                       utils::Vector{
+                           // Single scalar parameter
+                           b.Param("value", CreateASTTypeFor(ctx, mat_type->type())),
+                       },
+                       CreateASTTypeFor(ctx, mat_type),
+                       utils::Vector{
+                           b.Return(build_mat([&](uint32_t, uint32_t) {  //
+                               return b.Expr("value");
+                           })),
+                       });
                 return name;
             });
-            return ctx.dst->Call(fn, ctx.Clone(args[0]->Declaration()));
+            return b.Call(fn, ctx.Clone(args[0]->Declaration()));
         }
 
         if (args.Length() == mat_type->columns() * mat_type->rows()) {
@@ -123,12 +134,13 @@
             });
         }
 
-        TINT_ICE(Transform, ctx.dst->Diagnostics())
+        TINT_ICE(Transform, b.Diagnostics())
             << "matrix initializer has unexpected number of arguments";
         return nullptr;
     });
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/vectorize_scalar_matrix_initializers.h b/src/tint/transform/vectorize_scalar_matrix_initializers.h
index 342754a..f9c0164 100644
--- a/src/tint/transform/vectorize_scalar_matrix_initializers.h
+++ b/src/tint/transform/vectorize_scalar_matrix_initializers.h
@@ -29,19 +29,10 @@
     /// Destructor
     ~VectorizeScalarMatrixInitializers() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/vertex_pulling.cc b/src/tint/transform/vertex_pulling.cc
index 00b5a06..d5ee424 100644
--- a/src/tint/transform/vertex_pulling.cc
+++ b/src/tint/transform/vertex_pulling.cc
@@ -201,13 +201,46 @@
     return {BaseType::kInvalid, 0};
 }
 
-struct State {
-    State(CloneContext& context, const VertexPulling::Config& c) : ctx(context), cfg(c) {}
-    State(const State&) = default;
-    ~State() = default;
+}  // namespace
 
-    /// LocationReplacement describes an ast::Variable replacement for a
-    /// location input.
+/// PIMPL state for the transform
+struct VertexPulling::State {
+    /// Constructor
+    /// @param program the source program
+    /// @param c the VertexPulling config
+    State(const Program* program, const VertexPulling::Config& c) : src(program), cfg(c) {}
+
+    /// Runs the transform
+    /// @returns the new program or SkipTransform if the transform is not required
+    ApplyResult Run() {
+        // Find entry point
+        const ast::Function* func = nullptr;
+        for (auto* fn : src->AST().Functions()) {
+            if (fn->PipelineStage() == ast::PipelineStage::kVertex) {
+                if (func != nullptr) {
+                    b.Diagnostics().add_error(
+                        diag::System::Transform,
+                        "VertexPulling found more than one vertex entry point");
+                    return Program(std::move(b));
+                }
+                func = fn;
+            }
+        }
+        if (func == nullptr) {
+            b.Diagnostics().add_error(diag::System::Transform,
+                                      "Vertex stage entry point not found");
+            return Program(std::move(b));
+        }
+
+        AddVertexStorageBuffers();
+        Process(func);
+
+        ctx.Clone();
+        return Program(std::move(b));
+    }
+
+  private:
+    /// LocationReplacement describes an ast::Variable replacement for a location input.
     struct LocationReplacement {
         /// The variable to replace in the source Program
         ast::Variable* from;
@@ -215,13 +248,22 @@
         ast::Variable* to;
     };
 
+    /// LocationInfo describes an input location
     struct LocationInfo {
+        /// A builder that builds the expression that resolves to the (transformed) input location
         std::function<const ast::Expression*()> expr;
+        /// The store type of the location variable
         const sem::Type* type;
     };
 
-    CloneContext& ctx;
+    /// The source program
+    const Program* const src;
+    /// The transform config
     VertexPulling::Config const cfg;
+    /// The target program builder
+    ProgramBuilder b;
+    /// The clone context
+    CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
     std::unordered_map<uint32_t, LocationInfo> location_info;
     std::function<const ast::Expression*()> vertex_index_expr = nullptr;
     std::function<const ast::Expression*()> instance_index_expr = nullptr;
@@ -235,7 +277,7 @@
     Symbol GetVertexBufferName(uint32_t index) {
         return utils::GetOrCreate(vertex_buffer_names, index, [&] {
             static const char kVertexBufferNamePrefix[] = "tint_pulling_vertex_buffer_";
-            return ctx.dst->Symbols().New(kVertexBufferNamePrefix + std::to_string(index));
+            return b.Symbols().New(kVertexBufferNamePrefix + std::to_string(index));
         });
     }
 
@@ -243,7 +285,7 @@
     Symbol GetStructBufferName() {
         if (!struct_buffer_name.IsValid()) {
             static const char kStructBufferName[] = "tint_vertex_data";
-            struct_buffer_name = ctx.dst->Symbols().New(kStructBufferName);
+            struct_buffer_name = b.Symbols().New(kStructBufferName);
         }
         return struct_buffer_name;
     }
@@ -252,21 +294,19 @@
     void AddVertexStorageBuffers() {
         // Creating the struct type
         static const char kStructName[] = "TintVertexData";
-        auto* struct_type =
-            ctx.dst->Structure(ctx.dst->Symbols().New(kStructName),
-                               utils::Vector{
-                                   ctx.dst->Member(GetStructBufferName(), ctx.dst->ty.array<u32>()),
-                               });
+        auto* struct_type = b.Structure(b.Symbols().New(kStructName),
+                                        utils::Vector{
+                                            b.Member(GetStructBufferName(), b.ty.array<u32>()),
+                                        });
         for (uint32_t i = 0; i < cfg.vertex_state.size(); ++i) {
             // The decorated variable with struct type
-            ctx.dst->GlobalVar(GetVertexBufferName(i), ctx.dst->ty.Of(struct_type),
-                               ast::AddressSpace::kStorage, ast::Access::kRead,
-                               ctx.dst->Binding(AInt(i)), ctx.dst->Group(AInt(cfg.pulling_group)));
+            b.GlobalVar(GetVertexBufferName(i), b.ty.Of(struct_type), ast::AddressSpace::kStorage,
+                        ast::Access::kRead, b.Binding(AInt(i)), b.Group(AInt(cfg.pulling_group)));
         }
     }
 
     /// Creates and returns the assignment to the variables from the buffers
-    ast::BlockStatement* CreateVertexPullingPreamble() {
+    const ast::BlockStatement* CreateVertexPullingPreamble() {
         // Assign by looking at the vertex descriptor to find attributes with
         // matching location.
 
@@ -276,7 +316,7 @@
             const VertexBufferLayoutDescriptor& buffer_layout = cfg.vertex_state[buffer_idx];
 
             if ((buffer_layout.array_stride & 3) != 0) {
-                ctx.dst->Diagnostics().add_error(
+                b.Diagnostics().add_error(
                     diag::System::Transform,
                     "WebGPU requires that vertex stride must be a multiple of 4 bytes, "
                     "but VertexPulling array stride for buffer " +
@@ -292,15 +332,15 @@
             // buffer_array_base is the base array offset for all the vertex
             // attributes. These are units of uint (4 bytes).
             auto buffer_array_base =
-                ctx.dst->Symbols().New("buffer_array_base_" + std::to_string(buffer_idx));
+                b.Symbols().New("buffer_array_base_" + std::to_string(buffer_idx));
 
             auto* attribute_offset = index_expr;
             if (buffer_layout.array_stride != 4) {
-                attribute_offset = ctx.dst->Mul(index_expr, u32(buffer_layout.array_stride / 4u));
+                attribute_offset = b.Mul(index_expr, u32(buffer_layout.array_stride / 4u));
             }
 
             // let pulling_offset_n = <attribute_offset>
-            stmts.Push(ctx.dst->Decl(ctx.dst->Let(buffer_array_base, attribute_offset)));
+            stmts.Push(b.Decl(b.Let(buffer_array_base, attribute_offset)));
 
             for (const VertexAttributeDescriptor& attribute_desc : buffer_layout.attributes) {
                 auto it = location_info.find(attribute_desc.shader_location);
@@ -320,8 +360,8 @@
                     err << "VertexAttributeDescriptor for location "
                         << std::to_string(attribute_desc.shader_location) << " has format "
                         << attribute_desc.format << " but shader expects "
-                        << var.type->FriendlyName(ctx.src->Symbols());
-                    ctx.dst->Diagnostics().add_error(diag::System::Transform, err.str());
+                        << var.type->FriendlyName(src->Symbols());
+                    b.Diagnostics().add_error(diag::System::Transform, err.str());
                     return nullptr;
                 }
 
@@ -337,16 +377,16 @@
                     // WGSL variable vector width is smaller than the loaded vector width
                     switch (var_dt.width) {
                         case 1:
-                            value = ctx.dst->MemberAccessor(fetch, "x");
+                            value = b.MemberAccessor(fetch, "x");
                             break;
                         case 2:
-                            value = ctx.dst->MemberAccessor(fetch, "xy");
+                            value = b.MemberAccessor(fetch, "xy");
                             break;
                         case 3:
-                            value = ctx.dst->MemberAccessor(fetch, "xyz");
+                            value = b.MemberAccessor(fetch, "xyz");
                             break;
                         default:
-                            TINT_UNREACHABLE(Transform, ctx.dst->Diagnostics()) << var_dt.width;
+                            TINT_UNREACHABLE(Transform, b.Diagnostics()) << var_dt.width;
                             return nullptr;
                     }
                 } else if (var_dt.width > fmt_dt.width) {
@@ -355,32 +395,32 @@
                     utils::Vector<const ast::Expression*, 8> values{fetch};
                     switch (var_dt.base_type) {
                         case BaseType::kI32:
-                            ty = ctx.dst->ty.i32();
+                            ty = b.ty.i32();
                             for (uint32_t i = fmt_dt.width; i < var_dt.width; i++) {
-                                values.Push(ctx.dst->Expr((i == 3) ? 1_i : 0_i));
+                                values.Push(b.Expr((i == 3) ? 1_i : 0_i));
                             }
                             break;
                         case BaseType::kU32:
-                            ty = ctx.dst->ty.u32();
+                            ty = b.ty.u32();
                             for (uint32_t i = fmt_dt.width; i < var_dt.width; i++) {
-                                values.Push(ctx.dst->Expr((i == 3) ? 1_u : 0_u));
+                                values.Push(b.Expr((i == 3) ? 1_u : 0_u));
                             }
                             break;
                         case BaseType::kF32:
-                            ty = ctx.dst->ty.f32();
+                            ty = b.ty.f32();
                             for (uint32_t i = fmt_dt.width; i < var_dt.width; i++) {
-                                values.Push(ctx.dst->Expr((i == 3) ? 1_f : 0_f));
+                                values.Push(b.Expr((i == 3) ? 1_f : 0_f));
                             }
                             break;
                         default:
-                            TINT_UNREACHABLE(Transform, ctx.dst->Diagnostics()) << var_dt.base_type;
+                            TINT_UNREACHABLE(Transform, b.Diagnostics()) << var_dt.base_type;
                             return nullptr;
                     }
-                    value = ctx.dst->Construct(ctx.dst->ty.vec(ty, var_dt.width), values);
+                    value = b.Construct(b.ty.vec(ty, var_dt.width), values);
                 }
 
                 // Assign the value to the WGSL variable
-                stmts.Push(ctx.dst->Assign(var.expr(), value));
+                stmts.Push(b.Assign(var.expr(), value));
             }
         }
 
@@ -388,7 +428,7 @@
             return nullptr;
         }
 
-        return ctx.dst->create<ast::BlockStatement>(std::move(stmts));
+        return b.Block(std::move(stmts));
     }
 
     /// Generates an expression reading from a buffer a specific format.
@@ -407,7 +447,7 @@
         };
 
         // Returns a i32 loaded from buffer_base + offset.
-        auto load_i32 = [&] { return ctx.dst->Bitcast<i32>(load_u32()); };
+        auto load_i32 = [&] { return b.Bitcast<i32>(load_u32()); };
 
         // Returns a u32 loaded from buffer_base + offset + 4.
         auto load_next_u32 = [&] {
@@ -415,7 +455,7 @@
         };
 
         // Returns a i32 loaded from buffer_base + offset + 4.
-        auto load_next_i32 = [&] { return ctx.dst->Bitcast<i32>(load_next_u32()); };
+        auto load_next_i32 = [&] { return b.Bitcast<i32>(load_next_u32()); };
 
         // Returns a u16 loaded from offset, packed in the high 16 bits of a u32.
         // The low 16 bits are 0.
@@ -427,17 +467,17 @@
                 LoadPrimitive(array_base, low_u32_offset, buffer, VertexFormat::kUint32);
             switch (offset & 3) {
                 case 0:
-                    return ctx.dst->Shl(low_u32, 16_u);
+                    return b.Shl(low_u32, 16_u);
                 case 1:
-                    return ctx.dst->And(ctx.dst->Shl(low_u32, 8_u), 0xffff0000_u);
+                    return b.And(b.Shl(low_u32, 8_u), 0xffff0000_u);
                 case 2:
-                    return ctx.dst->And(low_u32, 0xffff0000_u);
+                    return b.And(low_u32, 0xffff0000_u);
                 default: {  // 3:
                     auto* high_u32 = LoadPrimitive(array_base, low_u32_offset + 4, buffer,
                                                    VertexFormat::kUint32);
-                    auto* shr = ctx.dst->Shr(low_u32, 8_u);
-                    auto* shl = ctx.dst->Shl(high_u32, 24_u);
-                    return ctx.dst->And(ctx.dst->Or(shl, shr), 0xffff0000_u);
+                    auto* shr = b.Shr(low_u32, 8_u);
+                    auto* shl = b.Shl(high_u32, 24_u);
+                    return b.And(b.Or(shl, shr), 0xffff0000_u);
                 }
             }
         };
@@ -450,24 +490,24 @@
                 LoadPrimitive(array_base, low_u32_offset, buffer, VertexFormat::kUint32);
             switch (offset & 3) {
                 case 0:
-                    return ctx.dst->And(low_u32, 0xffff_u);
+                    return b.And(low_u32, 0xffff_u);
                 case 1:
-                    return ctx.dst->And(ctx.dst->Shr(low_u32, 8_u), 0xffff_u);
+                    return b.And(b.Shr(low_u32, 8_u), 0xffff_u);
                 case 2:
-                    return ctx.dst->Shr(low_u32, 16_u);
+                    return b.Shr(low_u32, 16_u);
                 default: {  // 3:
                     auto* high_u32 = LoadPrimitive(array_base, low_u32_offset + 4, buffer,
                                                    VertexFormat::kUint32);
-                    auto* shr = ctx.dst->Shr(low_u32, 24_u);
-                    auto* shl = ctx.dst->Shl(high_u32, 8_u);
-                    return ctx.dst->And(ctx.dst->Or(shl, shr), 0xffff_u);
+                    auto* shr = b.Shr(low_u32, 24_u);
+                    auto* shl = b.Shl(high_u32, 8_u);
+                    return b.And(b.Or(shl, shr), 0xffff_u);
                 }
             }
         };
 
         // Returns a i16 loaded from offset, packed in the high 16 bits of a u32.
         // The low 16 bits are 0.
-        auto load_i16_h = [&] { return ctx.dst->Bitcast<i32>(load_u16_h()); };
+        auto load_i16_h = [&] { return b.Bitcast<i32>(load_u16_h()); };
 
         // Assumptions are made that alignment must be at least as large as the size
         // of a single component.
@@ -480,128 +520,121 @@
 
                 // Vectors of basic primitives
             case VertexFormat::kUint32x2:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.u32(),
-                               VertexFormat::kUint32, 2);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.u32(), VertexFormat::kUint32, 2);
             case VertexFormat::kUint32x3:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.u32(),
-                               VertexFormat::kUint32, 3);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.u32(), VertexFormat::kUint32, 3);
             case VertexFormat::kUint32x4:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.u32(),
-                               VertexFormat::kUint32, 4);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.u32(), VertexFormat::kUint32, 4);
             case VertexFormat::kSint32x2:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.i32(),
-                               VertexFormat::kSint32, 2);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.i32(), VertexFormat::kSint32, 2);
             case VertexFormat::kSint32x3:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.i32(),
-                               VertexFormat::kSint32, 3);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.i32(), VertexFormat::kSint32, 3);
             case VertexFormat::kSint32x4:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.i32(),
-                               VertexFormat::kSint32, 4);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.i32(), VertexFormat::kSint32, 4);
             case VertexFormat::kFloat32x2:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.f32(),
-                               VertexFormat::kFloat32, 2);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.f32(), VertexFormat::kFloat32,
+                               2);
             case VertexFormat::kFloat32x3:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.f32(),
-                               VertexFormat::kFloat32, 3);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.f32(), VertexFormat::kFloat32,
+                               3);
             case VertexFormat::kFloat32x4:
-                return LoadVec(array_base, offset, buffer, 4, ctx.dst->ty.f32(),
-                               VertexFormat::kFloat32, 4);
+                return LoadVec(array_base, offset, buffer, 4, b.ty.f32(), VertexFormat::kFloat32,
+                               4);
 
             case VertexFormat::kUint8x2: {
                 // yyxx0000, yyxx0000
-                auto* u16s = ctx.dst->vec2<u32>(load_u16_h());
+                auto* u16s = b.vec2<u32>(load_u16_h());
                 // xx000000, yyxx0000
-                auto* shl = ctx.dst->Shl(u16s, ctx.dst->vec2<u32>(8_u, 0_u));
+                auto* shl = b.Shl(u16s, b.vec2<u32>(8_u, 0_u));
                 // 000000xx, 000000yy
-                return ctx.dst->Shr(shl, ctx.dst->vec2<u32>(24_u));
+                return b.Shr(shl, b.vec2<u32>(24_u));
             }
             case VertexFormat::kUint8x4: {
                 // wwzzyyxx, wwzzyyxx, wwzzyyxx, wwzzyyxx
-                auto* u32s = ctx.dst->vec4<u32>(load_u32());
+                auto* u32s = b.vec4<u32>(load_u32());
                 // xx000000, yyxx0000, zzyyxx00, wwzzyyxx
-                auto* shl = ctx.dst->Shl(u32s, ctx.dst->vec4<u32>(24_u, 16_u, 8_u, 0_u));
+                auto* shl = b.Shl(u32s, b.vec4<u32>(24_u, 16_u, 8_u, 0_u));
                 // 000000xx, 000000yy, 000000zz, 000000ww
-                return ctx.dst->Shr(shl, ctx.dst->vec4<u32>(24_u));
+                return b.Shr(shl, b.vec4<u32>(24_u));
             }
             case VertexFormat::kUint16x2: {
                 // yyyyxxxx, yyyyxxxx
-                auto* u32s = ctx.dst->vec2<u32>(load_u32());
+                auto* u32s = b.vec2<u32>(load_u32());
                 // xxxx0000, yyyyxxxx
-                auto* shl = ctx.dst->Shl(u32s, ctx.dst->vec2<u32>(16_u, 0_u));
+                auto* shl = b.Shl(u32s, b.vec2<u32>(16_u, 0_u));
                 // 0000xxxx, 0000yyyy
-                return ctx.dst->Shr(shl, ctx.dst->vec2<u32>(16_u));
+                return b.Shr(shl, b.vec2<u32>(16_u));
             }
             case VertexFormat::kUint16x4: {
                 // yyyyxxxx, wwwwzzzz
-                auto* u32s = ctx.dst->vec2<u32>(load_u32(), load_next_u32());
+                auto* u32s = b.vec2<u32>(load_u32(), load_next_u32());
                 // yyyyxxxx, yyyyxxxx, wwwwzzzz, wwwwzzzz
-                auto* xxyy = ctx.dst->MemberAccessor(u32s, "xxyy");
+                auto* xxyy = b.MemberAccessor(u32s, "xxyy");
                 // xxxx0000, yyyyxxxx, zzzz0000, wwwwzzzz
-                auto* shl = ctx.dst->Shl(xxyy, ctx.dst->vec4<u32>(16_u, 0_u, 16_u, 0_u));
+                auto* shl = b.Shl(xxyy, b.vec4<u32>(16_u, 0_u, 16_u, 0_u));
                 // 0000xxxx, 0000yyyy, 0000zzzz, 0000wwww
-                return ctx.dst->Shr(shl, ctx.dst->vec4<u32>(16_u));
+                return b.Shr(shl, b.vec4<u32>(16_u));
             }
             case VertexFormat::kSint8x2: {
                 // yyxx0000, yyxx0000
-                auto* i16s = ctx.dst->vec2<i32>(load_i16_h());
+                auto* i16s = b.vec2<i32>(load_i16_h());
                 // xx000000, yyxx0000
-                auto* shl = ctx.dst->Shl(i16s, ctx.dst->vec2<u32>(8_u, 0_u));
+                auto* shl = b.Shl(i16s, b.vec2<u32>(8_u, 0_u));
                 // ssssssxx, ssssssyy
-                return ctx.dst->Shr(shl, ctx.dst->vec2<u32>(24_u));
+                return b.Shr(shl, b.vec2<u32>(24_u));
             }
             case VertexFormat::kSint8x4: {
                 // wwzzyyxx, wwzzyyxx, wwzzyyxx, wwzzyyxx
-                auto* i32s = ctx.dst->vec4<i32>(load_i32());
+                auto* i32s = b.vec4<i32>(load_i32());
                 // xx000000, yyxx0000, zzyyxx00, wwzzyyxx
-                auto* shl = ctx.dst->Shl(i32s, ctx.dst->vec4<u32>(24_u, 16_u, 8_u, 0_u));
+                auto* shl = b.Shl(i32s, b.vec4<u32>(24_u, 16_u, 8_u, 0_u));
                 // ssssssxx, ssssssyy, sssssszz, ssssssww
-                return ctx.dst->Shr(shl, ctx.dst->vec4<u32>(24_u));
+                return b.Shr(shl, b.vec4<u32>(24_u));
             }
             case VertexFormat::kSint16x2: {
                 // yyyyxxxx, yyyyxxxx
-                auto* i32s = ctx.dst->vec2<i32>(load_i32());
+                auto* i32s = b.vec2<i32>(load_i32());
                 // xxxx0000, yyyyxxxx
-                auto* shl = ctx.dst->Shl(i32s, ctx.dst->vec2<u32>(16_u, 0_u));
+                auto* shl = b.Shl(i32s, b.vec2<u32>(16_u, 0_u));
                 // ssssxxxx, ssssyyyy
-                return ctx.dst->Shr(shl, ctx.dst->vec2<u32>(16_u));
+                return b.Shr(shl, b.vec2<u32>(16_u));
             }
             case VertexFormat::kSint16x4: {
                 // yyyyxxxx, wwwwzzzz
-                auto* i32s = ctx.dst->vec2<i32>(load_i32(), load_next_i32());
+                auto* i32s = b.vec2<i32>(load_i32(), load_next_i32());
                 // yyyyxxxx, yyyyxxxx, wwwwzzzz, wwwwzzzz
-                auto* xxyy = ctx.dst->MemberAccessor(i32s, "xxyy");
+                auto* xxyy = b.MemberAccessor(i32s, "xxyy");
                 // xxxx0000, yyyyxxxx, zzzz0000, wwwwzzzz
-                auto* shl = ctx.dst->Shl(xxyy, ctx.dst->vec4<u32>(16_u, 0_u, 16_u, 0_u));
+                auto* shl = b.Shl(xxyy, b.vec4<u32>(16_u, 0_u, 16_u, 0_u));
                 // ssssxxxx, ssssyyyy, sssszzzz, sssswwww
-                return ctx.dst->Shr(shl, ctx.dst->vec4<u32>(16_u));
+                return b.Shr(shl, b.vec4<u32>(16_u));
             }
             case VertexFormat::kUnorm8x2:
-                return ctx.dst->MemberAccessor(ctx.dst->Call("unpack4x8unorm", load_u16_l()), "xy");
+                return b.MemberAccessor(b.Call("unpack4x8unorm", load_u16_l()), "xy");
             case VertexFormat::kSnorm8x2:
-                return ctx.dst->MemberAccessor(ctx.dst->Call("unpack4x8snorm", load_u16_l()), "xy");
+                return b.MemberAccessor(b.Call("unpack4x8snorm", load_u16_l()), "xy");
             case VertexFormat::kUnorm8x4:
-                return ctx.dst->Call("unpack4x8unorm", load_u32());
+                return b.Call("unpack4x8unorm", load_u32());
             case VertexFormat::kSnorm8x4:
-                return ctx.dst->Call("unpack4x8snorm", load_u32());
+                return b.Call("unpack4x8snorm", load_u32());
             case VertexFormat::kUnorm16x2:
-                return ctx.dst->Call("unpack2x16unorm", load_u32());
+                return b.Call("unpack2x16unorm", load_u32());
             case VertexFormat::kSnorm16x2:
-                return ctx.dst->Call("unpack2x16snorm", load_u32());
+                return b.Call("unpack2x16snorm", load_u32());
             case VertexFormat::kFloat16x2:
-                return ctx.dst->Call("unpack2x16float", load_u32());
+                return b.Call("unpack2x16float", load_u32());
             case VertexFormat::kUnorm16x4:
-                return ctx.dst->vec4<f32>(ctx.dst->Call("unpack2x16unorm", load_u32()),
-                                          ctx.dst->Call("unpack2x16unorm", load_next_u32()));
+                return b.vec4<f32>(b.Call("unpack2x16unorm", load_u32()),
+                                   b.Call("unpack2x16unorm", load_next_u32()));
             case VertexFormat::kSnorm16x4:
-                return ctx.dst->vec4<f32>(ctx.dst->Call("unpack2x16snorm", load_u32()),
-                                          ctx.dst->Call("unpack2x16snorm", load_next_u32()));
+                return b.vec4<f32>(b.Call("unpack2x16snorm", load_u32()),
+                                   b.Call("unpack2x16snorm", load_next_u32()));
             case VertexFormat::kFloat16x4:
-                return ctx.dst->vec4<f32>(ctx.dst->Call("unpack2x16float", load_u32()),
-                                          ctx.dst->Call("unpack2x16float", load_next_u32()));
+                return b.vec4<f32>(b.Call("unpack2x16float", load_u32()),
+                                   b.Call("unpack2x16float", load_next_u32()));
         }
 
-        TINT_UNREACHABLE(Transform, ctx.dst->Diagnostics())
-            << "format " << static_cast<int>(format);
+        TINT_UNREACHABLE(Transform, b.Diagnostics()) << "format " << static_cast<int>(format);
         return nullptr;
     }
 
@@ -623,12 +656,12 @@
 
             const ast ::Expression* index = nullptr;
             if (offset > 0) {
-                index = ctx.dst->Add(array_base, u32(offset / 4));
+                index = b.Add(array_base, u32(offset / 4));
             } else {
-                index = ctx.dst->Expr(array_base);
+                index = b.Expr(array_base);
             }
-            u = ctx.dst->IndexAccessor(
-                ctx.dst->MemberAccessor(GetVertexBufferName(buffer), GetStructBufferName()), index);
+            u = b.IndexAccessor(
+                b.MemberAccessor(GetVertexBufferName(buffer), GetStructBufferName()), index);
 
         } else {
             // Unaligned load
@@ -639,22 +672,22 @@
 
             uint32_t shift = 8u * (offset & 3u);
 
-            auto* low_shr = ctx.dst->Shr(low, u32(shift));
-            auto* high_shl = ctx.dst->Shl(high, u32(32u - shift));
-            u = ctx.dst->Or(low_shr, high_shl);
+            auto* low_shr = b.Shr(low, u32(shift));
+            auto* high_shl = b.Shl(high, u32(32u - shift));
+            u = b.Or(low_shr, high_shl);
         }
 
         switch (format) {
             case VertexFormat::kUint32:
                 return u;
             case VertexFormat::kSint32:
-                return ctx.dst->Bitcast(ctx.dst->ty.i32(), u);
+                return b.Bitcast(b.ty.i32(), u);
             case VertexFormat::kFloat32:
-                return ctx.dst->Bitcast(ctx.dst->ty.f32(), u);
+                return b.Bitcast(b.ty.f32(), u);
             default:
                 break;
         }
-        TINT_UNREACHABLE(Transform, ctx.dst->Diagnostics())
+        TINT_UNREACHABLE(Transform, b.Diagnostics())
             << "invalid format for LoadPrimitive" << static_cast<int>(format);
         return nullptr;
     }
@@ -682,8 +715,7 @@
             expr_list.Push(LoadPrimitive(array_base, primitive_offset, buffer, base_format));
         }
 
-        return ctx.dst->Construct(ctx.dst->create<ast::Vector>(base_type, count),
-                                  std::move(expr_list));
+        return b.Construct(b.create<ast::Vector>(base_type, count), std::move(expr_list));
     }
 
     /// Process a non-struct entry point parameter.
@@ -696,34 +728,30 @@
             // Create a function-scope variable to replace the parameter.
             auto func_var_sym = ctx.Clone(param->symbol);
             auto* func_var_type = ctx.Clone(param->type);
-            auto* func_var = ctx.dst->Var(func_var_sym, func_var_type);
-            ctx.InsertFront(func->body->statements, ctx.dst->Decl(func_var));
+            auto* func_var = b.Var(func_var_sym, func_var_type);
+            ctx.InsertFront(func->body->statements, b.Decl(func_var));
             // Capture mapping from location to the new variable.
             LocationInfo info;
-            info.expr = [this, func_var]() { return ctx.dst->Expr(func_var); };
+            info.expr = [this, func_var]() { return b.Expr(func_var); };
 
-            auto* sem = ctx.src->Sem().Get<sem::Parameter>(param);
+            auto* sem = src->Sem().Get<sem::Parameter>(param);
             info.type = sem->Type();
 
             if (!sem->Location().has_value()) {
-                TINT_ICE(Transform, ctx.dst->Diagnostics()) << "Location missing value";
+                TINT_ICE(Transform, b.Diagnostics()) << "Location missing value";
                 return;
             }
             location_info[sem->Location().value()] = info;
         } else if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) {
             // Check for existing vertex_index and instance_index builtins.
             if (builtin->builtin == ast::BuiltinValue::kVertexIndex) {
-                vertex_index_expr = [this, param]() {
-                    return ctx.dst->Expr(ctx.Clone(param->symbol));
-                };
+                vertex_index_expr = [this, param]() { return b.Expr(ctx.Clone(param->symbol)); };
             } else if (builtin->builtin == ast::BuiltinValue::kInstanceIndex) {
-                instance_index_expr = [this, param]() {
-                    return ctx.dst->Expr(ctx.Clone(param->symbol));
-                };
+                instance_index_expr = [this, param]() { return b.Expr(ctx.Clone(param->symbol)); };
             }
             new_function_parameters.Push(ctx.Clone(param));
         } else {
-            TINT_ICE(Transform, ctx.dst->Diagnostics()) << "Invalid entry point parameter";
+            TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
         }
     }
 
@@ -746,7 +774,7 @@
         for (auto* member : struct_ty->members) {
             auto member_sym = ctx.Clone(member->symbol);
             std::function<const ast::Expression*()> member_expr = [this, param_sym, member_sym]() {
-                return ctx.dst->MemberAccessor(param_sym, member_sym);
+                return b.MemberAccessor(param_sym, member_sym);
             };
 
             if (ast::HasAttribute<ast::LocationAttribute>(member->attributes)) {
@@ -754,7 +782,7 @@
                 LocationInfo info;
                 info.expr = member_expr;
 
-                auto* sem = ctx.src->Sem().Get(member);
+                auto* sem = src->Sem().Get(member);
                 info.type = sem->Type();
 
                 TINT_ASSERT(Transform, sem->Location().has_value());
@@ -770,7 +798,7 @@
                 }
                 members_to_clone.Push(member);
             } else {
-                TINT_ICE(Transform, ctx.dst->Diagnostics()) << "Invalid entry point parameter";
+                TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
             }
         }
 
@@ -781,8 +809,8 @@
         }
 
         // Create a function-scope variable to replace the parameter.
-        auto* func_var = ctx.dst->Var(param_sym, ctx.Clone(param->type));
-        ctx.InsertFront(func->body->statements, ctx.dst->Decl(func_var));
+        auto* func_var = b.Var(param_sym, ctx.Clone(param->type));
+        ctx.InsertFront(func->body->statements, b.Decl(func_var));
 
         if (!members_to_clone.IsEmpty()) {
             // Create a new struct without the location attributes.
@@ -791,20 +819,20 @@
                 auto member_sym = ctx.Clone(member->symbol);
                 auto* member_type = ctx.Clone(member->type);
                 auto member_attrs = ctx.Clone(member->attributes);
-                new_members.Push(ctx.dst->Member(member_sym, member_type, std::move(member_attrs)));
+                new_members.Push(b.Member(member_sym, member_type, std::move(member_attrs)));
             }
-            auto* new_struct = ctx.dst->Structure(ctx.dst->Sym(), new_members);
+            auto* new_struct = b.Structure(b.Sym(), new_members);
 
             // Create a new function parameter with this struct.
-            auto* new_param = ctx.dst->Param(ctx.dst->Sym(), ctx.dst->ty.Of(new_struct));
+            auto* new_param = b.Param(b.Sym(), b.ty.Of(new_struct));
             new_function_parameters.Push(new_param);
 
             // Copy values from the new parameter to the function-scope variable.
             for (auto* member : members_to_clone) {
                 auto member_name = ctx.Clone(member->symbol);
                 ctx.InsertFront(func->body->statements,
-                                ctx.dst->Assign(ctx.dst->MemberAccessor(func_var, member_name),
-                                                ctx.dst->MemberAccessor(new_param, member_name)));
+                                b.Assign(b.MemberAccessor(func_var, member_name),
+                                         b.MemberAccessor(new_param, member_name)));
             }
         }
     }
@@ -818,7 +846,7 @@
 
         // Process entry point parameters.
         for (auto* param : func->params) {
-            auto* sem = ctx.src->Sem().Get(param);
+            auto* sem = src->Sem().Get(param);
             if (auto* str = sem->Type()->As<sem::Struct>()) {
                 ProcessStructParameter(func, param, str->Declaration());
             } else {
@@ -830,11 +858,11 @@
         if (!vertex_index_expr) {
             for (const VertexBufferLayoutDescriptor& layout : cfg.vertex_state) {
                 if (layout.step_mode == VertexStepMode::kVertex) {
-                    auto name = ctx.dst->Symbols().New("tint_pulling_vertex_index");
-                    new_function_parameters.Push(ctx.dst->Param(
-                        name, ctx.dst->ty.u32(),
-                        utils::Vector{ctx.dst->Builtin(ast::BuiltinValue::kVertexIndex)}));
-                    vertex_index_expr = [this, name]() { return ctx.dst->Expr(name); };
+                    auto name = b.Symbols().New("tint_pulling_vertex_index");
+                    new_function_parameters.Push(
+                        b.Param(name, b.ty.u32(),
+                                utils::Vector{b.Builtin(ast::BuiltinValue::kVertexIndex)}));
+                    vertex_index_expr = [this, name]() { return b.Expr(name); };
                     break;
                 }
             }
@@ -842,11 +870,11 @@
         if (!instance_index_expr) {
             for (const VertexBufferLayoutDescriptor& layout : cfg.vertex_state) {
                 if (layout.step_mode == VertexStepMode::kInstance) {
-                    auto name = ctx.dst->Symbols().New("tint_pulling_instance_index");
-                    new_function_parameters.Push(ctx.dst->Param(
-                        name, ctx.dst->ty.u32(),
-                        utils::Vector{ctx.dst->Builtin(ast::BuiltinValue::kInstanceIndex)}));
-                    instance_index_expr = [this, name]() { return ctx.dst->Expr(name); };
+                    auto name = b.Symbols().New("tint_pulling_instance_index");
+                    new_function_parameters.Push(
+                        b.Param(name, b.ty.u32(),
+                                utils::Vector{b.Builtin(ast::BuiltinValue::kInstanceIndex)}));
+                    instance_index_expr = [this, name]() { return b.Expr(name); };
                     break;
                 }
             }
@@ -864,53 +892,24 @@
         auto attrs = ctx.Clone(func->attributes);
         auto ret_attrs = ctx.Clone(func->return_type_attributes);
         auto* new_func =
-            ctx.dst->create<ast::Function>(func->source, func_sym, new_function_parameters,
-                                           ret_type, body, std::move(attrs), std::move(ret_attrs));
+            b.create<ast::Function>(func->source, func_sym, new_function_parameters, ret_type, body,
+                                    std::move(attrs), std::move(ret_attrs));
         ctx.Replace(func, new_func);
     }
 };
 
-}  // namespace
-
 VertexPulling::VertexPulling() = default;
 VertexPulling::~VertexPulling() = default;
 
-void VertexPulling::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) const {
+Transform::ApplyResult VertexPulling::Apply(const Program* src,
+                                            const DataMap& inputs,
+                                            DataMap&) const {
     auto cfg = cfg_;
     if (auto* cfg_data = inputs.Get<Config>()) {
         cfg = *cfg_data;
     }
 
-    // Find entry point
-    const ast::Function* func = nullptr;
-    for (auto* fn : ctx.src->AST().Functions()) {
-        if (fn->PipelineStage() == ast::PipelineStage::kVertex) {
-            if (func != nullptr) {
-                ctx.dst->Diagnostics().add_error(
-                    diag::System::Transform,
-                    "VertexPulling found more than one vertex entry point");
-                return;
-            }
-            func = fn;
-        }
-    }
-    if (func == nullptr) {
-        ctx.dst->Diagnostics().add_error(diag::System::Transform,
-                                         "Vertex stage entry point not found");
-        return;
-    }
-
-    // TODO(idanr): Need to check shader locations in descriptor cover all
-    // attributes
-
-    // TODO(idanr): Make sure we covered all error cases, to guarantee the
-    // following stages will pass
-
-    State state{ctx, cfg};
-    state.AddVertexStorageBuffers();
-    state.Process(func);
-
-    ctx.Clone();
+    return State{src, cfg}.Run();
 }
 
 VertexPulling::Config::Config() = default;
diff --git a/src/tint/transform/vertex_pulling.h b/src/tint/transform/vertex_pulling.h
index 6dd35bc..c0f88a5 100644
--- a/src/tint/transform/vertex_pulling.h
+++ b/src/tint/transform/vertex_pulling.h
@@ -171,16 +171,14 @@
     /// Destructor
     ~VertexPulling() override;
 
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
+    struct State;
+
     Config cfg_;
 };
 
diff --git a/src/tint/transform/while_to_loop.cc b/src/tint/transform/while_to_loop.cc
index 45944e6..d359d2e 100644
--- a/src/tint/transform/while_to_loop.cc
+++ b/src/tint/transform/while_to_loop.cc
@@ -14,18 +14,17 @@
 
 #include "src/tint/transform/while_to_loop.h"
 
+#include <utility>
+
 #include "src/tint/ast/break_statement.h"
 #include "src/tint/program_builder.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::transform::WhileToLoop);
 
 namespace tint::transform {
+namespace {
 
-WhileToLoop::WhileToLoop() = default;
-
-WhileToLoop::~WhileToLoop() = default;
-
-bool WhileToLoop::ShouldRun(const Program* program, const DataMap&) const {
+bool ShouldRun(const Program* program) {
     for (auto* node : program->ASTNodes().Objects()) {
         if (node->Is<ast::WhileStatement>()) {
             return true;
@@ -34,20 +33,32 @@
     return false;
 }
 
-void WhileToLoop::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+}  // namespace
+
+WhileToLoop::WhileToLoop() = default;
+
+WhileToLoop::~WhileToLoop() = default;
+
+Transform::ApplyResult WhileToLoop::Apply(const Program* src, const DataMap&, DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
+    }
+
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
     ctx.ReplaceAll([&](const ast::WhileStatement* w) -> const ast::Statement* {
         utils::Vector<const ast::Statement*, 16> stmts;
         auto* cond = w->condition;
 
         // !condition
-        auto* not_cond =
-            ctx.dst->create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, ctx.Clone(cond));
+        auto* not_cond = b.Not(ctx.Clone(cond));
 
         // { break; }
-        auto* break_body = ctx.dst->Block(ctx.dst->create<ast::BreakStatement>());
+        auto* break_body = b.Block(b.Break());
 
         // if (!condition) { break; }
-        stmts.Push(ctx.dst->If(not_cond, break_body));
+        stmts.Push(b.If(not_cond, break_body));
 
         for (auto* stmt : w->body->statements) {
             stmts.Push(ctx.Clone(stmt));
@@ -55,13 +66,14 @@
 
         const ast::BlockStatement* continuing = nullptr;
 
-        auto* body = ctx.dst->Block(stmts);
-        auto* loop = ctx.dst->create<ast::LoopStatement>(body, continuing);
+        auto* body = b.Block(stmts);
+        auto* loop = b.Loop(body, continuing);
 
         return loop;
     });
 
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/while_to_loop.h b/src/tint/transform/while_to_loop.h
index 4915d68..187799a 100644
--- a/src/tint/transform/while_to_loop.h
+++ b/src/tint/transform/while_to_loop.h
@@ -29,19 +29,10 @@
     /// Destructor
     ~WhileToLoop() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 };
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/zero_init_workgroup_memory.cc b/src/tint/transform/zero_init_workgroup_memory.cc
index ea65436..ed3584e 100644
--- a/src/tint/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/transform/zero_init_workgroup_memory.cc
@@ -31,10 +31,24 @@
 TINT_INSTANTIATE_TYPEINFO(tint::transform::ZeroInitWorkgroupMemory);
 
 namespace tint::transform {
+namespace {
+
+bool ShouldRun(const Program* program) {
+    for (auto* global : program->AST().GlobalVariables()) {
+        if (auto* var = global->As<ast::Var>()) {
+            if (var->declared_address_space == ast::AddressSpace::kWorkgroup) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+}  // namespace
 
 using StatementList = utils::Vector<const ast::Statement*, 8>;
 
-/// PIMPL state for the ZeroInitWorkgroupMemory transform
+/// PIMPL state for the transform
 struct ZeroInitWorkgroupMemory::State {
     /// The clone context
     CloneContext& ctx;
@@ -424,24 +438,24 @@
 
 ZeroInitWorkgroupMemory::~ZeroInitWorkgroupMemory() = default;
 
-bool ZeroInitWorkgroupMemory::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* global : program->AST().GlobalVariables()) {
-        if (auto* var = global->As<ast::Var>()) {
-            if (var->declared_address_space == ast::AddressSpace::kWorkgroup) {
-                return true;
-            }
-        }
+Transform::ApplyResult ZeroInitWorkgroupMemory::Apply(const Program* src,
+                                                      const DataMap&,
+                                                      DataMap&) const {
+    if (!ShouldRun(src)) {
+        return SkipTransform;
     }
-    return false;
-}
 
-void ZeroInitWorkgroupMemory::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    for (auto* fn : ctx.src->AST().Functions()) {
+    ProgramBuilder b;
+    CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
+
+    for (auto* fn : src->AST().Functions()) {
         if (fn->PipelineStage() == ast::PipelineStage::kCompute) {
             State{ctx}.Run(fn);
         }
     }
+
     ctx.Clone();
+    return Program(std::move(b));
 }
 
 }  // namespace tint::transform
diff --git a/src/tint/transform/zero_init_workgroup_memory.h b/src/tint/transform/zero_init_workgroup_memory.h
index 07feaa8..64f4da8 100644
--- a/src/tint/transform/zero_init_workgroup_memory.h
+++ b/src/tint/transform/zero_init_workgroup_memory.h
@@ -30,19 +30,10 @@
     /// Destructor
     ~ZeroInitWorkgroupMemory() override;
 
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+    /// @copydoc Transform::Apply
+    ApplyResult Apply(const Program* program,
+                      const DataMap& inputs,
+                      DataMap& outputs) const override;
 
   private:
     struct State;
diff --git a/src/tint/utils/hashmap.h b/src/tint/utils/hashmap.h
index 87a0dd9..1040e0c 100644
--- a/src/tint/utils/hashmap.h
+++ b/src/tint/utils/hashmap.h
@@ -19,255 +19,119 @@
 #include <optional>
 #include <utility>
 
+#include "src/tint/debug.h"
 #include "src/tint/utils/hash.h"
-#include "src/tint/utils/hashset.h"
+#include "src/tint/utils/hashmap_base.h"
+#include "src/tint/utils/vector.h"
 
 namespace tint::utils {
 
 /// An unordered map that uses a robin-hood hashing algorithm.
-///
-/// Hashmap internally wraps a Hashset for providing a store for key-value pairs.
-///
-/// @see Hashset
-template <typename K,
-          typename V,
+template <typename KEY,
+          typename VALUE,
           size_t N,
-          typename HASH = Hasher<K>,
-          typename EQUAL = std::equal_to<K>>
-class Hashmap {
-    /// Entry holds a key and value pair, and is used as the element type of the underlying Hashset.
-    /// Entries are compared and hashed using only the #key.
-    /// @see Hasher
-    /// @see Equality
-    struct Entry {
-        /// Constructor from a key and value pair
-        Entry(K k, V v) : key(std::move(k)), value(std::move(v)) {}
-
-        /// Copy-constructor.
-        Entry(const Entry&) = default;
-
-        /// Move-constructor.
-        Entry(Entry&&) = default;
-
-        /// Copy-assignment operator
-        Entry& operator=(const Entry&) = default;
-
-        /// Move-assignment operator
-        Entry& operator=(Entry&&) = default;
-
-        K key;    /// The map entry key
-        V value;  /// The map entry value
-    };
-
-    /// Hash provider for the underlying Hashset.
-    /// Provides hash functions for an Entry or K.
-    /// The hash functions only consider the key of an entry.
-    struct Hasher {
-        /// Calculates a hash from an Entry
-        size_t operator()(const Entry& entry) const { return HASH()(entry.key); }
-        /// Calculates a hash from a K
-        size_t operator()(const K& key) const { return HASH()(key); }
-    };
-
-    /// Equality provider for the underlying Hashset.
-    /// Provides equality functions for an Entry or K to an Entry.
-    /// The equality functions only consider the key for equality.
-    struct Equality {
-        /// Compares an Entry to an Entry for equality.
-        bool operator()(const Entry& a, const Entry& b) const { return EQUAL()(a.key, b.key); }
-        /// Compares a K to an Entry for equality.
-        bool operator()(const K& a, const Entry& b) const { return EQUAL()(a, b.key); }
-    };
-
-    /// The underlying set
-    using Set = Hashset<Entry, N, Hasher, Equality>;
+          typename HASH = Hasher<KEY>,
+          typename EQUAL = std::equal_to<KEY>>
+class Hashmap : public HashmapBase<KEY, VALUE, N, HASH, EQUAL> {
+    using Base = HashmapBase<KEY, VALUE, N, HASH, EQUAL>;
+    using PutMode = typename Base::PutMode;
 
   public:
-    /// A Key and Value const-reference pair.
-    struct KeyValue {
-        /// key of a map entry
-        const K& key;
-        /// value of a map entry
-        const V& value;
+    /// The key type
+    using Key = KEY;
+    /// The value type
+    using Value = VALUE;
+    /// The key-value type for a map entry
+    using Entry = KeyValue<Key, Value>;
 
-        /// Equality operator
-        /// @param other the other KeyValue
-        /// @returns true if the key and value of this KeyValue are equal to other's.
-        bool operator==(const KeyValue& other) const {
-            return key == other.key && value == other.value;
-        }
-    };
+    /// Result of Add()
+    using AddResult = typename Base::PutResult;
 
-    /// STL-style alias to KeyValue.
-    /// Used by gmock for the `ElementsAre` checks.
-    using value_type = KeyValue;
-
-    /// Iterator for the map.
-    /// Iterators are invalidated if the map is modified.
-    class Iterator {
-      public:
-        /// @returns the key of the entry pointed to by this iterator
-        const K& Key() const { return it->key; }
-
-        /// @returns the value of the entry pointed to by this iterator
-        const V& Value() const { return it->value; }
-
-        /// Increments the iterator
-        /// @returns this iterator
-        Iterator& operator++() {
-            ++it;
-            return *this;
-        }
-
-        /// Equality operator
-        /// @param other the other iterator to compare this iterator to
-        /// @returns true if this iterator is equal to other
-        bool operator==(const Iterator& other) const { return it == other.it; }
-
-        /// Inequality operator
-        /// @param other the other iterator to compare this iterator to
-        /// @returns true if this iterator is not equal to other
-        bool operator!=(const Iterator& other) const { return it != other.it; }
-
-        /// @returns a pair of key and value for the entry pointed to by this iterator
-        KeyValue operator*() const { return {Key(), Value()}; }
-
-      private:
-        /// Friend class
-        friend class Hashmap;
-
-        /// Underlying iterator type
-        using SetIterator = typename Set::Iterator;
-
-        explicit Iterator(SetIterator i) : it(i) {}
-
-        SetIterator it;
-    };
-
-    /// Removes all entries from the map.
-    void Clear() { set_.Clear(); }
-
-    /// Adds the key-value pair to the map, if the map does not already contain an entry with a key
-    /// equal to `key`.
-    /// @param key the entry's key to add to the map
-    /// @param value the entry's value to add to the map
-    /// @returns true if the entry was added to the map, false if there was already an entry in the
-    ///          map with a key equal to `key`.
-    template <typename KEY, typename VALUE>
-    bool Add(KEY&& key, VALUE&& value) {
-        return set_.Add(Entry{std::forward<KEY>(key), std::forward<VALUE>(value)});
+    /// Adds a value to the map, if the map does not already contain an entry with the key @p key.
+    /// @param key the entry key.
+    /// @param value the value of the entry to add to the map.
+    /// @returns A AddResult describing the result of the add
+    template <typename K, typename V>
+    AddResult Add(K&& key, V&& value) {
+        return this->template Put<PutMode::kAdd>(std::forward<K>(key), std::forward<V>(value));
     }
 
-    /// Adds the key-value pair to the map, replacing any entry with a key equal to `key`.
-    /// @param key the entry's key to add to the map
-    /// @param value the entry's value to add to the map
-    template <typename KEY, typename VALUE>
-    void Replace(KEY&& key, VALUE&& value) {
-        set_.Replace(Entry{std::forward<KEY>(key), std::forward<VALUE>(value)});
+    /// Adds a new entry to the map, replacing any entry that has a key equal to @p key.
+    /// @param key the entry key.
+    /// @param value the value of the entry to add to the map.
+    /// @returns A AddResult describing the result of the replace
+    template <typename K, typename V>
+    AddResult Replace(K&& key, V&& value) {
+        return this->template Put<PutMode::kReplace>(std::forward<K>(key), std::forward<V>(value));
     }
 
-    /// Searches for an entry with the given key value.
-    /// @param key the entry's key value to search for.
-    /// @returns the value of the entry with the given key, or no value if the entry was not found.
-    std::optional<V> Get(const K& key) {
-        if (auto* entry = set_.Find(key)) {
-            return entry->value;
+    /// @param key the key to search for.
+    /// @returns the value of the entry that is equal to `value`, or no value if the entry was not
+    ///          found.
+    std::optional<Value> Get(const Key& key) const {
+        if (auto [found, index] = this->IndexOf(key); found) {
+            return this->slots_[index].entry->value;
         }
         return std::nullopt;
     }
 
-    /// Searches for an entry with the given key value, adding and returning the result of
-    /// calling `create` if the entry was not found.
+    /// Searches for an entry with the given key, adding and returning the result of calling
+    /// @p create if the entry was not found.
     /// @note: Before calling `create`, the map will insert a zero-initialized value for the given
-    /// key, which will be replaced with the value returned by `create`. If `create` adds an entry
-    /// with `key` to this map, it will be replaced.
+    /// key, which will be replaced with the value returned by @p create. If @p create adds an entry
+    /// with @p key to this map, it will be replaced.
     /// @param key the entry's key value to search for.
     /// @param create the create function to call if the map does not contain the key.
     /// @returns the value of the entry.
-    template <typename CREATE>
-    V& GetOrCreate(const K& key, CREATE&& create) {
-        auto res = set_.Add(Entry{key, V{}});
-        if (res.action == AddAction::kAdded) {
-            // Store the set generation before calling create()
-            auto generation = set_.Generation();
+    template <typename K, typename CREATE>
+    Value& GetOrCreate(K&& key, CREATE&& create) {
+        auto res = Add(std::forward<K>(key), Value{});
+        if (res.action == MapAction::kAdded) {
+            // Store the map generation before calling create()
+            auto generation = this->Generation();
             // Call create(), which might modify this map.
             auto value = create();
             // Was this map mutated?
-            if (set_.Generation() == generation) {
+            if (this->Generation() == generation) {
                 // Calling create() did not touch the map. No need to lookup again.
-                res.entry->value = std::move(value);
+                *res.value = std::move(value);
             } else {
                 // Calling create() modified the map. Need to insert again.
-                res = set_.Replace(Entry{key, std::move(value)});
+                res = Replace(key, std::move(value));
             }
         }
-        return res.entry->value;
+        return *res.value;
     }
 
     /// Searches for an entry with the given key value, adding and returning a newly created
     /// zero-initialized value if the entry was not found.
     /// @param key the entry's key value to search for.
     /// @returns the value of the entry.
-    V& GetOrZero(const K& key) {
-        auto res = set_.Add(Entry{key, V{}});
-        return res.entry->value;
+    template <typename K>
+    Value& GetOrZero(K&& key) {
+        auto res = Add(std::forward<K>(key), Value{});
+        return *res.value;
     }
 
-    /// Searches for an entry with the given key value.
-    /// @param key the entry's key value to search for.
-    /// @returns the a pointer to the value of the entry with the given key, or nullptr if the entry
-    /// was not found.
-    /// @warning the pointer must not be used after the map is mutated
-    V* Find(const K& key) {
-        if (auto* entry = set_.Find(key)) {
-            return &entry->value;
+    /// @param key the key to search for.
+    /// @returns a pointer to the entry that is equal to the given value, or nullptr if the map does
+    ///          not contain the given value.
+    const Value* Find(const Key& key) const {
+        if (auto [found, index] = this->IndexOf(key); found) {
+            return &this->slots_[index].entry->value;
         }
         return nullptr;
     }
 
-    /// Searches for an entry with the given key value.
-    /// @param key the entry's key value to search for.
-    /// @returns the a pointer to the value of the entry with the given key, or nullptr if the entry
-    /// was not found.
-    /// @warning the pointer must not be used after the map is mutated
-    const V* Find(const K& key) const {
-        if (auto* entry = set_.Find(key)) {
-            return &entry->value;
+    /// @param key the key to search for.
+    /// @returns a pointer to the entry that is equal to the given value, or nullptr if the map does
+    ///          not contain the given value.
+    Value* Find(const Key& key) {
+        if (auto [found, index] = this->IndexOf(key); found) {
+            return &this->slots_[index].entry->value;
         }
         return nullptr;
     }
-
-    /// Removes an entry from the set with a key equal to `key`.
-    /// @param key the entry key value to remove.
-    /// @returns true if an entry was removed.
-    bool Remove(const K& key) { return set_.Remove(key); }
-
-    /// Checks whether an entry exists in the map with a key equal to `key`.
-    /// @param key the entry key value to search for.
-    /// @returns true if the map contains an entry with the given key.
-    bool Contains(const K& key) const { return set_.Contains(key); }
-
-    /// Pre-allocates memory so that the map can hold at least `capacity` entries.
-    /// @param capacity the new capacity of the map.
-    void Reserve(size_t capacity) { set_.Reserve(capacity); }
-
-    /// @returns the number of entries in the map.
-    size_t Count() const { return set_.Count(); }
-
-    /// @returns a monotonic counter which is incremented whenever the map is mutated.
-    size_t Generation() const { return set_.Generation(); }
-
-    /// @returns true if the map contains no entries.
-    bool IsEmpty() const { return set_.IsEmpty(); }
-
-    /// @returns an iterator to the start of the map
-    Iterator begin() const { return Iterator{set_.begin()}; }
-
-    /// @returns an iterator to the end of the map
-    Iterator end() const { return Iterator{set_.end()}; }
-
-  private:
-    Set set_;
 };
 
 }  // namespace tint::utils
diff --git a/src/tint/utils/hashmap_base.h b/src/tint/utils/hashmap_base.h
new file mode 100644
index 0000000..ca0712a
--- /dev/null
+++ b/src/tint/utils/hashmap_base.h
@@ -0,0 +1,570 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_UTILS_HASHMAP_BASE_H_
+#define SRC_TINT_UTILS_HASHMAP_BASE_H_
+
+#include <algorithm>
+#include <functional>
+#include <optional>
+#include <tuple>
+#include <utility>
+
+#include "src/tint/debug.h"
+#include "src/tint/utils/hash.h"
+#include "src/tint/utils/vector.h"
+
+namespace tint::utils {
+
+/// Action taken by a map mutation
+enum class MapAction {
+    /// A new entry was added to the map
+    kAdded,
+    /// A existing entry in the map was replaced
+    kReplaced,
+    /// No action was taken as the map already contained an entry with the given key
+    kKeptExisting,
+};
+
+/// KeyValue is a key-value pair.
+template <typename KEY, typename VALUE>
+struct KeyValue {
+    /// The key type
+    using Key = KEY;
+    /// The value type
+    using Value = VALUE;
+
+    /// The key
+    Key key;
+
+    /// The value
+    Value value;
+
+    /// Equality operator
+    /// @param other the RHS of the operator
+    /// @returns true if both the key and value of this KeyValue are equal to the key and value
+    /// of @p other
+    template <typename K, typename V>
+    bool operator==(const KeyValue<K, V>& other) const {
+        return key == other.key && value == other.value;
+    }
+
+    /// Inequality operator
+    /// @param other the RHS of the operator
+    /// @returns true if either the key and value of this KeyValue are not equal to the key and
+    /// value of @p other
+    template <typename K, typename V>
+    bool operator!=(const KeyValue<K, V>& other) const {
+        return *this != other;
+    }
+};
+
+/// Writes the KeyValue to the std::ostream.
+/// @param out the std::ostream to write to
+/// @param key_value the KeyValue to write
+/// @returns out so calls can be chained
+template <typename KEY, typename VALUE>
+std::ostream& operator<<(std::ostream& out, const KeyValue<KEY, VALUE>& key_value) {
+    return out << "[" << key_value.key << ": " << key_value.value << "]";
+}
+
+/// A base class for Hashmap and Hashset that uses a robin-hood hashing algorithm.
+/// @see the fantastic tutorial: https://programming.guide/robin-hood-hashing.html
+template <typename KEY,
+          typename VALUE,
+          size_t N,
+          typename HASH = Hasher<KEY>,
+          typename EQUAL = std::equal_to<KEY>>
+class HashmapBase {
+    static constexpr bool ValueIsVoid = std::is_same_v<VALUE, void>;
+
+  public:
+    /// The key type
+    using Key = KEY;
+    /// The value type
+    using Value = VALUE;
+    /// The entry type for the map.
+    /// This is:
+    /// - Key when Value is void (used by Hashset)
+    /// - KeyValue<Key, Value> when Value is void (used by Hashmap)
+    using Entry = std::conditional_t<ValueIsVoid, Key, KeyValue<Key, Value>>;
+
+    /// STL-friendly alias to Entry. Used by gmock.
+    using value_type = Entry;
+
+  private:
+    /// @returns the key from an entry
+    static const Key& KeyOf(const Entry& entry) {
+        if constexpr (ValueIsVoid) {
+            return entry;
+        } else {
+            return entry.key;
+        }
+    }
+
+    /// @returns a pointer to the value from an entry.
+    static Value* ValueOf(Entry& entry) {
+        if constexpr (ValueIsVoid) {
+            return nullptr;  // Hashset only has keys
+        } else {
+            return &entry.value;
+        }
+    }
+
+    /// A slot is a single entry in the underlying vector.
+    /// A slot can either be empty or filled with a value. If the slot is empty, #hash and #distance
+    /// will be zero.
+    struct Slot {
+        bool Equals(size_t key_hash, const Key& key) const {
+            return key_hash == hash && EQUAL()(key, KeyOf(*entry));
+        }
+
+        /// The slot value. If this does not contain a value, then the slot is vacant.
+        std::optional<Entry> entry;
+        /// The precomputed hash of value.
+        size_t hash = 0;
+        size_t distance = 0;
+    };
+
+    /// The target length of the underlying vector length in relation to the number of entries in
+    /// the map, expressed as a percentage. For example a value of `150` would mean there would be
+    /// at least 50% more slots than the number of map entries.
+    static constexpr size_t kRehashFactor = 150;
+
+    /// @returns the target slot vector size to hold `n` map entries.
+    static constexpr size_t NumSlots(size_t count) { return (count * kRehashFactor) / 100; }
+
+    /// The fixed-size slot vector length, based on N and kRehashFactor.
+    static constexpr size_t kNumFixedSlots = NumSlots(N);
+
+    /// The minimum number of slots for the map.
+    static constexpr size_t kMinSlots = std::max<size_t>(kNumFixedSlots, 4);
+
+  public:
+    /// Iterator for entries in the map.
+    /// Iterators are invalidated if the map is modified.
+    class Iterator {
+      public:
+        /// @returns the value pointed to by this iterator
+        const Entry* operator->() const { return &current->entry.value(); }
+
+        /// @returns a reference to the value at the iterator
+        const Entry& operator*() const { return current->entry.value(); }
+
+        /// Increments the iterator
+        /// @returns this iterator
+        Iterator& operator++() {
+            if (current == end) {
+                return *this;
+            }
+            current++;
+            SkipToNextValue();
+            return *this;
+        }
+
+        /// Equality operator
+        /// @param other the other iterator to compare this iterator to
+        /// @returns true if this iterator is equal to other
+        bool operator==(const Iterator& other) const { return current == other.current; }
+
+        /// Inequality operator
+        /// @param other the other iterator to compare this iterator to
+        /// @returns true if this iterator is not equal to other
+        bool operator!=(const Iterator& other) const { return current != other.current; }
+
+      private:
+        /// Friend class
+        friend class HashmapBase;
+
+        Iterator(const Slot* c, const Slot* e) : current(c), end(e) { SkipToNextValue(); }
+
+        /// Moves the iterator forward, stopping at the next slot that is not empty.
+        void SkipToNextValue() {
+            while (current != end && !current->entry.has_value()) {
+                current++;
+            }
+        }
+
+        const Slot* current;  /// The slot the iterator is pointing to
+        const Slot* end;      /// One past the last slot in the map
+    };
+
+    /// Constructor
+    HashmapBase() { slots_.Resize(kMinSlots); }
+
+    /// Copy constructor
+    /// @param other the other HashmapBase to copy
+    HashmapBase(const HashmapBase& other) = default;
+
+    /// Move constructor
+    /// @param other the other HashmapBase to move
+    HashmapBase(HashmapBase&& other) = default;
+
+    /// Destructor
+    ~HashmapBase() { Clear(); }
+
+    /// Copy-assignment operator
+    /// @param other the other HashmapBase to copy
+    /// @returns this so calls can be chained
+    HashmapBase& operator=(const HashmapBase& other) = default;
+
+    /// Move-assignment operator
+    /// @param other the other HashmapBase to move
+    /// @returns this so calls can be chained
+    HashmapBase& operator=(HashmapBase&& other) = default;
+
+    /// Removes all entries from the map.
+    void Clear() {
+        slots_.Clear();  // Destructs all entries
+        slots_.Resize(kMinSlots);
+        count_ = 0;
+        generation_++;
+    }
+
+    /// Removes an entry from the map.
+    /// @param key the entry key.
+    /// @returns true if an entry was removed.
+    bool Remove(const Key& key) {
+        const auto [found, start] = IndexOf(key);
+        if (!found) {
+            return false;
+        }
+
+        // Shuffle the entries backwards until we either find a free slot, or a slot that has zero
+        // distance.
+        Slot* prev = nullptr;
+        Scan(start, [&](size_t, size_t index) {
+            auto& slot = slots_[index];
+            if (prev) {
+                // note: `distance == 0` also includes empty slots.
+                if (slot.distance == 0) {
+                    // Clear the previous slot, and stop shuffling.
+                    *prev = {};
+                    return Action::kStop;
+                } else {
+                    // Shuffle the slot backwards.
+                    prev->entry = std::move(slot.entry);
+                    prev->hash = slot.hash;
+                    prev->distance = slot.distance - 1;
+                }
+            }
+            prev = &slot;
+            return Action::kContinue;
+        });
+
+        // Entry was removed.
+        count_--;
+        generation_++;
+
+        return true;
+    }
+
+    /// Checks whether an entry exists in the map
+    /// @param key the key to search for.
+    /// @returns true if the map contains an entry with the given value.
+    bool Contains(const Key& key) const {
+        const auto [found, _] = IndexOf(key);
+        return found;
+    }
+
+    /// Pre-allocates memory so that the map can hold at least `capacity` entries.
+    /// @param capacity the new capacity of the map.
+    void Reserve(size_t capacity) {
+        // Calculate the number of slots required to hold `capacity` entries.
+        const size_t num_slots = std::max(NumSlots(capacity), kMinSlots);
+        if (slots_.Length() >= num_slots) {
+            // Already have enough slots.
+            return;
+        }
+
+        // Move all the values out of the map and into a vector.
+        Vector<Entry, N> entries;
+        entries.Reserve(count_);
+        for (auto& slot : slots_) {
+            if (slot.entry.has_value()) {
+                entries.Push(std::move(slot.entry.value()));
+            }
+        }
+
+        // Clear the map, grow the number of slots.
+        Clear();
+        slots_.Resize(num_slots);
+
+        // As the number of slots has grown, the slot indices will have changed from before, so
+        // re-add all the entries back into the map.
+        for (auto& entry : entries) {
+            if constexpr (ValueIsVoid) {
+                struct NoValue {};
+                Put<PutMode::kAdd>(std::move(entry), NoValue{});
+            } else {
+                Put<PutMode::kAdd>(std::move(entry.key), std::move(entry.value));
+            }
+        }
+    }
+
+    /// @returns the number of entries in the map.
+    size_t Count() const { return count_; }
+
+    /// @returns true if the map contains no entries.
+    bool IsEmpty() const { return count_ == 0; }
+
+    /// @returns a monotonic counter which is incremented whenever the map is mutated.
+    size_t Generation() const { return generation_; }
+
+    /// @returns an iterator to the start of the map.
+    Iterator begin() const { return Iterator{slots_.begin(), slots_.end()}; }
+
+    /// @returns an iterator to the end of the map.
+    Iterator end() const { return Iterator{slots_.end(), slots_.end()}; }
+
+    /// A debug function for checking that the map is in good health.
+    /// Asserts if the map is corrupted.
+    void ValidateIntegrity() const {
+        size_t num_alive = 0;
+        for (size_t slot_idx = 0; slot_idx < slots_.Length(); slot_idx++) {
+            const auto& slot = slots_[slot_idx];
+            if (slot.entry.has_value()) {
+                num_alive++;
+                auto const [index, hash] = Hash(KeyOf(*slot.entry));
+                TINT_ASSERT(Utils, hash == slot.hash);
+                TINT_ASSERT(Utils, slot_idx == Wrap(index + slot.distance));
+            }
+        }
+        TINT_ASSERT(Utils, num_alive == count_);
+    }
+
+  protected:
+    /// The behaviour of Put() when an entry already exists with the given key.
+    enum class PutMode {
+        /// Do not replace existing entries with the new value.
+        kAdd,
+        /// Replace existing entries with the new value.
+        kReplace,
+    };
+
+    /// Result of Put()
+    struct PutResult {
+        /// Whether the insert replaced or added a new entry to the map.
+        MapAction action = MapAction::kAdded;
+        /// A pointer to the inserted entry value.
+        Value* value = nullptr;
+
+        /// @returns true if the entry was added to the map, or an existing entry was replaced.
+        operator bool() const { return action != MapAction::kKeptExisting; }
+    };
+
+    /// The common implementation for Add() and Replace()
+    /// @param key the key of the entry to add to the map.
+    /// @param value the value of the entry to add to the map.
+    /// @returns A PutResult describing the result of the insertion
+    template <PutMode MODE, typename K, typename V>
+    PutResult Put(K&& key, V&& value) {
+        // Ensure the map can fit a new entry
+        if (ShouldRehash(count_ + 1)) {
+            Reserve((count_ + 1) * 2);
+        }
+
+        const auto hash = Hash(key);
+
+        auto make_entry = [&]() {
+            if constexpr (ValueIsVoid) {
+                return std::forward<K>(key);
+            } else {
+                return Entry{std::forward<K>(key), std::forward<V>(value)};
+            }
+        };
+
+        PutResult result{};
+        Scan(hash.scan_start, [&](size_t distance, size_t index) {
+            auto& slot = slots_[index];
+            if (!slot.entry.has_value()) {
+                // Found an empty slot.
+                // Place value directly into the slot, and we're done.
+                slot.entry.emplace(make_entry());
+                slot.hash = hash.code;
+                slot.distance = distance;
+                count_++;
+                generation_++;
+                result = PutResult{MapAction::kAdded, ValueOf(*slot.entry)};
+                return Action::kStop;
+            }
+
+            // Slot has an entry
+
+            if (slot.Equals(hash.code, key)) {
+                // Slot is equal to value. Replace or preserve?
+                if constexpr (MODE == PutMode::kReplace) {
+                    slot.entry = make_entry();
+                    generation_++;
+                    result = PutResult{MapAction::kReplaced, ValueOf(*slot.entry)};
+                } else {
+                    result = PutResult{MapAction::kKeptExisting, ValueOf(*slot.entry)};
+                }
+                return Action::kStop;
+            }
+
+            if (slot.distance < distance) {
+                // Existing slot has a closer distance than the value we're attempting to insert.
+                // Steal from the rich!
+                // Move the current slot to a temporary (evicted), and put the value into the slot.
+                Slot evicted{make_entry(), hash.code, distance};
+                std::swap(evicted, slot);
+
+                // Find a new home for the evicted slot.
+                evicted.distance++;  // We've already swapped at index.
+                InsertShuffle(Wrap(index + 1), std::move(evicted));
+
+                count_++;
+                generation_++;
+                result = PutResult{MapAction::kAdded, ValueOf(*slot.entry)};
+
+                return Action::kStop;
+            }
+            return Action::kContinue;
+        });
+
+        return result;
+    }
+
+    /// Return type of the Scan() callback.
+    enum class Action {
+        /// Continue scanning for a slot
+        kContinue,
+        /// Immediately stop scanning for a slot
+        kStop,
+    };
+
+    /// Sequentially visits each of the slots starting with the slot with the index @p start,
+    /// calling the callback function @p f for each slot until @p f returns Action::kStop.
+    /// @param start the index of the first slot to start scanning from.
+    /// @param f the callback function which:
+    /// * must be a function with the signature `Action(size_t distance, size_t index)`.
+    /// * must return Action::kStop within one whole cycle of the slots.
+    template <typename F>
+    void Scan(size_t start, F&& f) const {
+        size_t distance = 0;
+        for (size_t index = start; index < slots_.Length(); index++) {
+            if (f(distance, index) == Action::kStop) {
+                return;
+            }
+            distance++;
+        }
+        for (size_t index = 0; index < start; index++) {
+            if (f(distance, index) == Action::kStop) {
+                return;
+            }
+            distance++;
+        }
+        tint::diag::List diags;
+        TINT_ICE(Utils, diags) << "HashmapBase::Scan() looped entire map without finding a slot";
+    }
+
+    /// HashResult is the return value of Hash()
+    struct HashResult {
+        /// The target (zero-distance) slot index for the key.
+        size_t scan_start;
+        /// The calculated hash code of the key.
+        size_t code;
+    };
+
+    /// @param key the key to hash
+    /// @returns a tuple holding the target slot index for the given value, and the hash of the
+    /// value, respectively.
+    HashResult Hash(const Key& key) const {
+        size_t hash = HASH()(key);
+        size_t index = Wrap(hash);
+        return {index, hash};
+    }
+
+    /// Looks for the key in the map.
+    /// @param key the key to search for.
+    /// @returns a tuple holding a boolean representing whether the key was found in the map, and
+    /// if found, the index of the slot that holds the key.
+    std::tuple<bool, size_t> IndexOf(const Key& key) const {
+        const auto hash = Hash(key);
+
+        bool found = false;
+        size_t idx = 0;
+
+        Scan(hash.scan_start, [&](size_t distance, size_t index) {
+            auto& slot = slots_[index];
+            if (!slot.entry.has_value()) {
+                return Action::kStop;
+            }
+            if (slot.Equals(hash.code, key)) {
+                found = true;
+                idx = index;
+                return Action::kStop;
+            }
+            if (slot.distance < distance) {
+                // If the slot distance is less than the current probe distance, then the slot must
+                // be for entry that has an index that comes after key. In this situation, we know
+                // that the map does not contain the key, as it would have been found before this
+                // slot. The "Lookup" section of https://programming.guide/robin-hood-hashing.html
+                // suggests that the condition should inverted, but this is wrong.
+                return Action::kStop;
+            }
+            return Action::kContinue;
+        });
+
+        return {found, idx};
+    }
+
+    /// Shuffles slots for an insertion that has been placed one slot before `start`.
+    /// @param start the index of the first slot to start shuffling.
+    /// @param evicted the slot content that was evicted for the insertion.
+    void InsertShuffle(size_t start, Slot evicted) {
+        Scan(start, [&](size_t, size_t index) {
+            auto& slot = slots_[index];
+
+            if (!slot.entry.has_value()) {
+                // Empty slot found for evicted.
+                slot = std::move(evicted);
+                return Action::kStop;  //  We're done.
+            }
+
+            if (slot.distance < evicted.distance) {
+                // Occupied slot has shorter distance to evicted.
+                // Swap slot and evicted.
+                std::swap(slot, evicted);
+            }
+
+            // evicted moves further from the target slot...
+            evicted.distance++;
+
+            return Action::kContinue;
+        });
+    }
+
+    /// @param count the number of new entries in the map
+    /// @returns true if the map should grow the slot vector, and rehash the items.
+    bool ShouldRehash(size_t count) const { return NumSlots(count) > slots_.Length(); }
+
+    /// @param index an input value
+    /// @returns the input value modulo the number of slots.
+    size_t Wrap(size_t index) const { return index % slots_.Length(); }
+
+    /// The vector of slots. The vector length is equal to its capacity.
+    Vector<Slot, kNumFixedSlots> slots_;
+
+    /// The number of entries in the map.
+    size_t count_ = 0;
+
+    /// Counter that's incremented with each modification to the map.
+    size_t generation_ = 0;
+};
+
+}  // namespace tint::utils
+
+#endif  // SRC_TINT_UTILS_HASHMAP_BASE_H_
diff --git a/src/tint/utils/hashmap_test.cc b/src/tint/utils/hashmap_test.cc
index 9a5b01e..77421cf 100644
--- a/src/tint/utils/hashmap_test.cc
+++ b/src/tint/utils/hashmap_test.cc
@@ -92,14 +92,14 @@
 
 TEST(Hashmap, Iterator) {
     using Map = Hashmap<int, std::string, 8>;
-    using KV = typename Map::KeyValue;
+    using Entry = typename Map::Entry;
     Map map;
     map.Add(1, "one");
     map.Add(4, "four");
     map.Add(3, "three");
     map.Add(2, "two");
-    EXPECT_THAT(map, testing::UnorderedElementsAre(KV{1, "one"}, KV{2, "two"}, KV{3, "three"},
-                                                   KV{4, "four"}));
+    EXPECT_THAT(map, testing::UnorderedElementsAre(Entry{1, "one"}, Entry{2, "two"},
+                                                   Entry{3, "three"}, Entry{4, "four"}));
 }
 
 TEST(Hashmap, AddMany) {
diff --git a/src/tint/utils/hashset.h b/src/tint/utils/hashset.h
index f7d5efe..53f71f5 100644
--- a/src/tint/utils/hashset.h
+++ b/src/tint/utils/hashset.h
@@ -23,497 +23,26 @@
 #include <utility>
 
 #include "src/tint/debug.h"
-#include "src/tint/utils/hash.h"
+#include "src/tint/utils/hashmap.h"
 #include "src/tint/utils/vector.h"
 
 namespace tint::utils {
 
-/// Action taken by Hashset::Insert()
-enum class AddAction {
-    /// Insert() added a new entry to the Hashset
-    kAdded,
-    /// Insert() replaced an existing entry in the Hashset
-    kReplaced,
-    /// Insert() found an existing entry, which was not replaced.
-    kKeptExisting,
-};
-
 /// An unordered set that uses a robin-hood hashing algorithm.
-/// @see the fantastic tutorial: https://programming.guide/robin-hood-hashing.html
-template <typename T, size_t N, typename HASH = Hasher<T>, typename EQUAL = std::equal_to<T>>
-class Hashset {
-    /// A slot is a single entry in the underlying vector.
-    /// A slot can either be empty or filled with a value. If the slot is empty, #hash and #distance
-    /// will be zero.
-    struct Slot {
-        template <typename V>
-        bool Equals(size_t value_hash, const V& val) const {
-            return value_hash == hash && EQUAL()(val, value.value());
-        }
-
-        /// The slot value. If this does not contain a value, then the slot is vacant.
-        std::optional<T> value;
-        /// The precomputed hash of value.
-        size_t hash = 0;
-        size_t distance = 0;
-    };
-
-    /// The target length of the underlying vector length in relation to the number of entries in
-    /// the set, expressed as a percentage. For example a value of `150` would mean there would be
-    /// at least 50% more slots than the number of set entries.
-    static constexpr size_t kRehashFactor = 150;
-
-    /// @returns the target slot vector size to hold `n` set entries.
-    static constexpr size_t NumSlots(size_t count) { return (count * kRehashFactor) / 100; }
-
-    /// The fixed-size slot vector length, based on N and kRehashFactor.
-    static constexpr size_t kNumFixedSlots = NumSlots(N);
-
-    /// The minimum number of slots for the set.
-    static constexpr size_t kMinSlots = std::max<size_t>(kNumFixedSlots, 4);
+template <typename KEY, size_t N, typename HASH = Hasher<KEY>, typename EQUAL = std::equal_to<KEY>>
+class Hashset : public HashmapBase<KEY, void, N, HASH, EQUAL> {
+    using Base = HashmapBase<KEY, void, N, HASH, EQUAL>;
+    using PutMode = typename Base::PutMode;
 
   public:
-    /// Iterator for entries in the set.
-    /// Iterators are invalidated if the set is modified.
-    class Iterator {
-      public:
-        /// @returns the value pointed to by this iterator
-        const T* operator->() const { return &current->value.value(); }
-
-        /// Increments the iterator
-        /// @returns this iterator
-        Iterator& operator++() {
-            if (current == end) {
-                return *this;
-            }
-            current++;
-            SkipToNextValue();
-            return *this;
-        }
-
-        /// Equality operator
-        /// @param other the other iterator to compare this iterator to
-        /// @returns true if this iterator is equal to other
-        bool operator==(const Iterator& other) const { return current == other.current; }
-
-        /// Inequality operator
-        /// @param other the other iterator to compare this iterator to
-        /// @returns true if this iterator is not equal to other
-        bool operator!=(const Iterator& other) const { return current != other.current; }
-
-        /// @returns a reference to the value at the iterator
-        const T& operator*() const { return current->value.value(); }
-
-      private:
-        /// Friend class
-        friend class Hashset;
-
-        Iterator(const Slot* c, const Slot* e) : current(c), end(e) { SkipToNextValue(); }
-
-        /// Moves the iterator forward, stopping at the next slot that is not empty.
-        void SkipToNextValue() {
-            while (current != end && !current->value.has_value()) {
-                current++;
-            }
-        }
-
-        const Slot* current;  /// The slot the iterator is pointing to
-        const Slot* end;      /// One past the last slot in the set
-    };
-
-    /// Type of `T`.
-    using value_type = T;
-
-    /// Constructor
-    Hashset() { slots_.Resize(kMinSlots); }
-
-    /// Copy constructor
-    /// @param other the other Hashset to copy
-    Hashset(const Hashset& other) = default;
-
-    /// Move constructor
-    /// @param other the other Hashset to move
-    Hashset(Hashset&& other) = default;
-
-    /// Destructor
-    ~Hashset() { Clear(); }
-
-    /// Copy-assignment operator
-    /// @param other the other Hashset to copy
-    /// @returns this so calls can be chained
-    Hashset& operator=(const Hashset& other) = default;
-
-    /// Move-assignment operator
-    /// @param other the other Hashset to move
-    /// @returns this so calls can be chained
-    Hashset& operator=(Hashset&& other) = default;
-
-    /// Removes all entries from the set.
-    void Clear() {
-        slots_.Clear();  // Destructs all entries
-        slots_.Resize(kMinSlots);
-        count_ = 0;
-        generation_++;
-    }
-
-    /// Result of Add()
-    struct AddResult {
-        /// Whether the insert replaced or added a new entry to the set.
-        AddAction action = AddAction::kAdded;
-        /// A pointer to the inserted entry.
-        /// @warning do not modify this pointer in a way that would cause the equality or hash of
-        ///          the entry to change. Doing this will corrupt the Hashset.
-        T* entry = nullptr;
-
-        /// @returns true if the entry was added to the set, or an existing entry was replaced.
-        operator bool() const { return action != AddAction::kKeptExisting; }
-    };
-
     /// Adds a value to the set, if the set does not already contain an entry equal to `value`.
     /// @param value the value to add to the set.
-    /// @returns A AddResult describing the result of the add
-    /// @warning do not modify the inserted entry in a way that would cause the equality of hash of
-    ///          the entry to change. Doing this will corrupt the Hashset.
+    /// @returns true if the value was added, false if there was an existing value in the set.
     template <typename V>
-    AddResult Add(V&& value) {
-        return Put<PutMode::kAdd>(std::forward<V>(value));
+    bool Add(V&& value) {
+        struct NoValue {};
+        return this->template Put<PutMode::kAdd>(std::forward<V>(value), NoValue{});
     }
-
-    /// Adds a value to the set, replacing any entry equal to `value`.
-    /// @param value the value to add to the set.
-    /// @returns A AddResult describing the result of the replace
-    template <typename V>
-    AddResult Replace(V&& value) {
-        return Put<PutMode::kReplace>(std::forward<V>(value));
-    }
-
-    /// Removes an entry from the set.
-    /// @param value the value to remove from the set.
-    /// @returns true if an entry was removed.
-    template <typename V>
-    bool Remove(const V& value) {
-        const auto [found, start] = IndexOf(value);
-        if (!found) {
-            return false;
-        }
-
-        // Shuffle the entries backwards until we either find a free slot, or a slot that has zero
-        // distance.
-        Slot* prev = nullptr;
-        Scan(start, [&](size_t, size_t index) {
-            auto& slot = slots_[index];
-            if (prev) {
-                // note: `distance == 0` also includes empty slots.
-                if (slot.distance == 0) {
-                    // Clear the previous slot, and stop shuffling.
-                    *prev = {};
-                    return Action::kStop;
-                } else {
-                    // Shuffle the slot backwards.
-                    prev->value = std::move(slot.value);
-                    prev->hash = slot.hash;
-                    prev->distance = slot.distance - 1;
-                }
-            }
-            prev = &slot;
-            return Action::kContinue;
-        });
-
-        // Entry was removed.
-        count_--;
-        generation_++;
-
-        return true;
-    }
-
-    /// @param value the value to search for.
-    /// @returns the value of the entry that is equal to `value`, or no value if the entry was not
-    ///          found.
-    template <typename V>
-    std::optional<T> Get(const V& value) const {
-        if (const auto [found, index] = IndexOf(value); found) {
-            return slots_[index].value.value();
-        }
-        return std::nullopt;
-    }
-
-    /// @param value the value to search for.
-    /// @returns a pointer to the entry that is equal to the given value, or nullptr if the set does
-    ///          not contain the given value.
-    template <typename V>
-    const T* Find(const V& value) const {
-        const auto [found, index] = IndexOf(value);
-        return found ? &slots_[index].value.value() : nullptr;
-    }
-
-    /// @param value the value to search for.
-    /// @returns a pointer to the entry that is equal to the given value, or nullptr if the set does
-    ///          not contain the given value.
-    /// @warning do not modify the inserted entry in a way that would cause the equality of hash of
-    ///          the entry to change. Doing this will corrupt the Hashset.
-    template <typename V>
-    T* Find(const V& value) {
-        const auto [found, index] = IndexOf(value);
-        return found ? &slots_[index].value.value() : nullptr;
-    }
-
-    /// Checks whether an entry exists in the set
-    /// @param value the value to search for.
-    /// @returns true if the set contains an entry with the given value.
-    template <typename V>
-    bool Contains(const V& value) const {
-        const auto [found, _] = IndexOf(value);
-        return found;
-    }
-
-    /// Pre-allocates memory so that the set can hold at least `capacity` entries.
-    /// @param capacity the new capacity of the set.
-    void Reserve(size_t capacity) {
-        // Calculate the number of slots required to hold `capacity` entries.
-        const size_t num_slots = std::max(NumSlots(capacity), kMinSlots);
-        if (slots_.Length() >= num_slots) {
-            // Already have enough slots.
-            return;
-        }
-
-        // Move all the values out of the set and into a vector.
-        Vector<T, N> values;
-        values.Reserve(count_);
-        for (auto& slot : slots_) {
-            if (slot.value.has_value()) {
-                values.Push(std::move(slot.value.value()));
-            }
-        }
-
-        // Clear the set, grow the number of slots.
-        Clear();
-        slots_.Resize(num_slots);
-
-        // As the number of slots has grown, the slot indices will have changed from before, so
-        // re-add all the values back into the set.
-        for (auto& value : values) {
-            Add(std::move(value));
-        }
-    }
-
-    /// @returns the number of entries in the set.
-    size_t Count() const { return count_; }
-
-    /// @returns true if the set contains no entries.
-    bool IsEmpty() const { return count_ == 0; }
-
-    /// @returns a monotonic counter which is incremented whenever the set is mutated.
-    size_t Generation() const { return generation_; }
-
-    /// @returns an iterator to the start of the set.
-    Iterator begin() const { return Iterator{slots_.begin(), slots_.end()}; }
-
-    /// @returns an iterator to the end of the set.
-    Iterator end() const { return Iterator{slots_.end(), slots_.end()}; }
-
-    /// A debug function for checking that the set is in good health.
-    /// Asserts if the set is corrupted.
-    void ValidateIntegrity() const {
-        size_t num_alive = 0;
-        for (size_t slot_idx = 0; slot_idx < slots_.Length(); slot_idx++) {
-            const auto& slot = slots_[slot_idx];
-            if (slot.value.has_value()) {
-                num_alive++;
-                auto const [index, hash] = Hash(slot.value.value());
-                TINT_ASSERT(Utils, hash == slot.hash);
-                TINT_ASSERT(Utils, slot_idx == Wrap(index + slot.distance));
-            }
-        }
-        TINT_ASSERT(Utils, num_alive == count_);
-    }
-
-  private:
-    /// The behaviour of Put() when an entry already exists with the given key.
-    enum class PutMode {
-        /// Do not replace existing entries with the new value.
-        kAdd,
-        /// Replace existing entries with the new value.
-        kReplace,
-    };
-    /// The common implementation for Add() and Replace()
-    /// @param value the value to add to the set.
-    /// @returns A AddResult describing the result of the insertion
-    template <PutMode MODE, typename V>
-    AddResult Put(V&& value) {
-        // Ensure the set can fit a new entry
-        if (ShouldRehash(count_ + 1)) {
-            Reserve((count_ + 1) * 2);
-        }
-
-        const auto hash = Hash(value);
-
-        AddResult result{};
-        Scan(hash.scan_start, [&](size_t distance, size_t index) {
-            auto& slot = slots_[index];
-            if (!slot.value.has_value()) {
-                // Found an empty slot.
-                // Place value directly into the slot, and we're done.
-                slot.value.emplace(std::forward<V>(value));
-                slot.hash = hash.value;
-                slot.distance = distance;
-                count_++;
-                generation_++;
-                result = AddResult{AddAction::kAdded, &slot.value.value()};
-                return Action::kStop;
-            }
-
-            // Slot has an entry
-
-            if (slot.Equals(hash.value, value)) {
-                // Slot is equal to value. Replace or preserve?
-                if constexpr (MODE == PutMode::kReplace) {
-                    slot.value = std::forward<V>(value);
-                    generation_++;
-                    result = AddResult{AddAction::kReplaced, &slot.value.value()};
-                } else {
-                    result = AddResult{AddAction::kKeptExisting, &slot.value.value()};
-                }
-                return Action::kStop;
-            }
-
-            if (slot.distance < distance) {
-                // Existing slot has a closer distance than the value we're attempting to insert.
-                // Steal from the rich!
-                // Move the current slot to a temporary (evicted), and put the value into the slot.
-                Slot evicted{std::forward<V>(value), hash.value, distance};
-                std::swap(evicted, slot);
-
-                // Find a new home for the evicted slot.
-                evicted.distance++;  // We've already swapped at index.
-                InsertShuffle(Wrap(index + 1), std::move(evicted));
-
-                count_++;
-                generation_++;
-                result = AddResult{AddAction::kAdded, &slot.value.value()};
-
-                return Action::kStop;
-            }
-            return Action::kContinue;
-        });
-
-        return result;
-    }
-
-    /// Return type of the Scan() callback.
-    enum class Action {
-        /// Continue scanning for a slot
-        kContinue,
-        /// Immediately stop scanning for a slot
-        kStop,
-    };
-
-    /// Sequentially visits each of the slots starting with the slot with the index `start`, calling
-    /// the callback function `f` for each slot until `f` returns Action::kStop.
-    /// `f` must be a function with the signature `Action(size_t distance, size_t index)`.
-    /// `f` must return Action::kStop within one whole cycle of the slots.
-    template <typename F>
-    void Scan(size_t start, F&& f) const {
-        size_t index = start;
-        for (size_t distance = 0; distance < slots_.Length(); distance++) {
-            if (f(distance, index) == Action::kStop) {
-                return;
-            }
-            index = Wrap(index + 1);
-        }
-        tint::diag::List diags;
-        TINT_ICE(Utils, diags) << "Hashset::Scan() looped entire set without finding a slot";
-    }
-
-    /// HashResult is the return value of Hash()
-    struct HashResult {
-        /// The target (zero-distance) slot index for the value.
-        size_t scan_start;
-        /// The calculated hash of the value.
-        size_t value;
-    };
-
-    /// @returns a tuple holding the target slot index for the given value, and the hash of the
-    ///          value, respectively.
-    template <typename V>
-    HashResult Hash(const V& value) const {
-        size_t hash = HASH()(value);
-        size_t index = Wrap(hash);
-        return {index, hash};
-    }
-
-    /// Looks for the value in the set.
-    /// @returns a tuple holding a boolean representing whether the value was found in the set, and
-    ///          if found, the index of the slot that holds the value.
-    template <typename V>
-    std::tuple<bool, size_t> IndexOf(const V& value) const {
-        const auto hash = Hash(value);
-
-        bool found = false;
-        size_t idx = 0;
-
-        Scan(hash.scan_start, [&](size_t distance, size_t index) {
-            auto& slot = slots_[index];
-            if (!slot.value.has_value()) {
-                return Action::kStop;
-            }
-            if (slot.Equals(hash.value, value)) {
-                found = true;
-                idx = index;
-                return Action::kStop;
-            }
-            if (slot.distance < distance) {
-                // If the slot distance is less than the current probe distance, then the slot must
-                // be for entry that has an index that comes after value. In this situation, we know
-                // that the set does not contain the value, as it would have been found before this
-                // slot. The "Lookup" section of https://programming.guide/robin-hood-hashing.html
-                // suggests that the condition should inverted, but this is wrong.
-                return Action::kStop;
-            }
-            return Action::kContinue;
-        });
-
-        return {found, idx};
-    }
-
-    /// Shuffles slots for an insertion that has been placed one slot before `start`.
-    /// @param evicted the slot content that was evicted for the insertion.
-    void InsertShuffle(size_t start, Slot evicted) {
-        Scan(start, [&](size_t, size_t index) {
-            auto& slot = slots_[index];
-
-            if (!slot.value.has_value()) {
-                // Empty slot found for evicted.
-                slot = std::move(evicted);
-                return Action::kStop;  //  We're done.
-            }
-
-            if (slot.distance < evicted.distance) {
-                // Occupied slot has shorter distance to evicted.
-                // Swap slot and evicted.
-                std::swap(slot, evicted);
-            }
-
-            // evicted moves further from the target slot...
-            evicted.distance++;
-
-            return Action::kContinue;
-        });
-    }
-
-    /// @returns true if the set should grow the slot vector, and rehash the items.
-    bool ShouldRehash(size_t count) const { return NumSlots(count) > slots_.Length(); }
-
-    /// Wrap returns the index value modulo the number of slots.
-    size_t Wrap(size_t index) const { return index % slots_.Length(); }
-
-    /// The vector of slots. The vector length is equal to its capacity.
-    Vector<Slot, kNumFixedSlots> slots_;
-
-    /// The number of entries in the set.
-    size_t count_ = 0;
-
-    /// Counter that's incremented with each modification to the set.
-    size_t generation_ = 0;
 };
 
 }  // namespace tint::utils
diff --git a/src/tint/utils/hashset_test.cc b/src/tint/utils/hashset_test.cc
index 6e8d1cc..64f0da3 100644
--- a/src/tint/utils/hashset_test.cc
+++ b/src/tint/utils/hashset_test.cc
@@ -74,18 +74,12 @@
     EXPECT_EQ(set.Generation(), 1u);
     set.Add(1);
     EXPECT_EQ(set.Generation(), 1u);
-    set.Replace(1);
-    EXPECT_EQ(set.Generation(), 2u);
     set.Add(2);
-    EXPECT_EQ(set.Generation(), 3u);
+    EXPECT_EQ(set.Generation(), 2u);
     set.Remove(1);
-    EXPECT_EQ(set.Generation(), 4u);
+    EXPECT_EQ(set.Generation(), 3u);
     set.Clear();
-    EXPECT_EQ(set.Generation(), 5u);
-    set.Find(2);
-    EXPECT_EQ(set.Generation(), 5u);
-    set.Get(2);
-    EXPECT_EQ(set.Generation(), 5u);
+    EXPECT_EQ(set.Generation(), 4u);
 }
 
 TEST(Hashset, Iterator) {
@@ -103,53 +97,30 @@
     Hashset<std::string, 8> set;
     for (size_t i = 0; i < 1000000; i++) {
         std::string value = std::to_string(rnd() & 0x100);
-        switch (rnd() % 8) {
+        switch (rnd() % 5) {
             case 0: {  // Add
                 auto expected = reference.emplace(value).second;
                 ASSERT_EQ(set.Add(value), expected) << "i: " << i;
                 ASSERT_TRUE(set.Contains(value)) << "i: " << i;
                 break;
             }
-            case 1: {  // Replace
-                reference.emplace(value);
-                set.Replace(value);
-                ASSERT_TRUE(set.Contains(value)) << "i: " << i;
-                break;
-            }
-            case 2: {  // Remove
+            case 1: {  // Remove
                 auto expected = reference.erase(value) != 0;
                 ASSERT_EQ(set.Remove(value), expected) << "i: " << i;
                 ASSERT_FALSE(set.Contains(value)) << "i: " << i;
                 break;
             }
-            case 3: {  // Contains
+            case 2: {  // Contains
                 auto expected = reference.count(value) != 0;
                 ASSERT_EQ(set.Contains(value), expected) << "i: " << i;
                 break;
             }
-            case 4: {  // Get
-                if (reference.count(value) != 0) {
-                    ASSERT_TRUE(set.Get(value).has_value()) << "i: " << i;
-                    ASSERT_EQ(set.Get(value), value) << "i: " << i;
-                } else {
-                    ASSERT_FALSE(set.Get(value).has_value()) << "i: " << i;
-                }
-                break;
-            }
-            case 5: {  // Find
-                if (reference.count(value) != 0) {
-                    ASSERT_EQ(*set.Find(value), value) << "i: " << i;
-                } else {
-                    ASSERT_EQ(set.Find(value), nullptr) << "i: " << i;
-                }
-                break;
-            }
-            case 6: {  // Copy / Move
+            case 3: {  // Copy / Move
                 Hashset<std::string, 8> tmp(set);
                 set = std::move(tmp);
                 break;
             }
-            case 7: {  // Clear
+            case 4: {  // Clear
                 reference.clear();
                 set.Clear();
                 break;
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index ab78722..50cdbcf 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -186,6 +186,7 @@
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.bitshift_modulo = true;
         polyfills.count_leading_zeros = true;
         polyfills.count_trailing_zeros = true;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
@@ -796,6 +797,9 @@
     if (builtin->Type() == sem::BuiltinType::kRadians) {
         return EmitRadiansCall(out, expr, builtin);
     }
+    if (builtin->Type() == sem::BuiltinType::kQuantizeToF16) {
+        return EmitQuantizeToF16Call(out, expr, builtin);
+    }
     if (builtin->Type() == sem::BuiltinType::kArrayLength) {
         return EmitArrayLength(out, expr);
     }
@@ -1287,6 +1291,38 @@
                              });
 }
 
+bool GeneratorImpl::EmitQuantizeToF16Call(std::ostream& out,
+                                          const ast::CallExpression* expr,
+                                          const sem::Builtin* builtin) {
+    // Emulate by casting to f16 and back again.
+    return CallBuiltinHelper(
+        out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
+            const auto v = params[0];
+            if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
+                switch (vec->Width()) {
+                    case 2: {
+                        line(b) << "return unpackHalf2x16(packHalf2x16(" << v << "));";
+                        return true;
+                    }
+                    case 3: {
+                        line(b) << "return vec3(";
+                        line(b) << "  unpackHalf2x16(packHalf2x16(" << v << ".xy)),";
+                        line(b) << "  unpackHalf2x16(packHalf2x16(" << v << ".zz)).x);";
+                        return true;
+                    }
+                    default: {
+                        line(b) << "return vec4(";
+                        line(b) << "  unpackHalf2x16(packHalf2x16(" << v << ".xy)),";
+                        line(b) << "  unpackHalf2x16(packHalf2x16(" << v << ".zw)));";
+                        return true;
+                    }
+                }
+            }
+            line(b) << "return unpackHalf2x16(packHalf2x16(vec2(" << v << "))).x;";
+            return true;
+        });
+}
+
 bool GeneratorImpl::EmitBarrierCall(std::ostream& out, const sem::Builtin* builtin) {
     // TODO(crbug.com/tint/661): Combine sequential barriers to a single
     // instruction.
diff --git a/src/tint/writer/glsl/generator_impl.h b/src/tint/writer/glsl/generator_impl.h
index 889b406..c34880d 100644
--- a/src/tint/writer/glsl/generator_impl.h
+++ b/src/tint/writer/glsl/generator_impl.h
@@ -270,6 +270,14 @@
     bool EmitRadiansCall(std::ostream& out,
                          const ast::CallExpression* expr,
                          const sem::Builtin* builtin);
+    /// Handles generating a call to the `quantizeToF16()` intrinsic
+    /// @param out the output of the expression stream
+    /// @param expr the call expression
+    /// @param builtin the semantic information for the builtin
+    /// @returns true if the call expression is emitted
+    bool EmitQuantizeToF16Call(std::ostream& out,
+                               const ast::CallExpression* expr,
+                               const sem::Builtin* builtin);
     /// Handles a case statement
     /// @param stmt the statement
     /// @returns true if the statement was emitted successfully
diff --git a/src/tint/writer/glsl/generator_impl_builtin_test.cc b/src/tint/writer/glsl/generator_impl_builtin_test.cc
index 511a309..61d97b0 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_test.cc
@@ -1266,5 +1266,118 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_Builtin, QuantizeToF16_Scalar) {
+    GlobalVar("v", Expr(2_f), ast::AddressSpace::kPrivate);
+    WrapInFunction(Call("quantizeToF16", "v"));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+float tint_quantizeToF16(float param_0) {
+  return unpackHalf2x16(packHalf2x16(vec2(param_0))).x;
+}
+
+
+float v = 2.0f;
+void test_function() {
+  float tint_symbol = tint_quantizeToF16(v);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, QuantizeToF16_Vec2) {
+    GlobalVar("v", vec2<f32>(2_f), ast::AddressSpace::kPrivate);
+    WrapInFunction(Call("quantizeToF16", "v"));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+vec2 tint_quantizeToF16(vec2 param_0) {
+  return unpackHalf2x16(packHalf2x16(param_0));
+}
+
+
+vec2 v = vec2(2.0f);
+void test_function() {
+  vec2 tint_symbol = tint_quantizeToF16(v);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, QuantizeToF16_Vec3) {
+    GlobalVar("v", vec3<f32>(2_f), ast::AddressSpace::kPrivate);
+    WrapInFunction(Call("quantizeToF16", "v"));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+vec3 tint_quantizeToF16(vec3 param_0) {
+  return vec3(
+    unpackHalf2x16(packHalf2x16(param_0.xy)),
+    unpackHalf2x16(packHalf2x16(param_0.zz)).x);
+}
+
+
+vec3 v = vec3(2.0f);
+void test_function() {
+  vec3 tint_symbol = tint_quantizeToF16(v);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
+
+TEST_F(GlslGeneratorImplTest_Builtin, QuantizeToF16_Vec4) {
+    GlobalVar("v", vec4<f32>(2_f), ast::AddressSpace::kPrivate);
+    WrapInFunction(Call("quantizeToF16", "v"));
+
+    GeneratorImpl& gen = SanitizeAndBuild();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+vec4 tint_quantizeToF16(vec4 param_0) {
+  return vec4(
+    unpackHalf2x16(packHalf2x16(param_0.xy)),
+    unpackHalf2x16(packHalf2x16(param_0.zw)));
+}
+
+
+vec4 v = vec4(2.0f);
+void test_function() {
+  vec4 tint_symbol = tint_quantizeToF16(v);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  test_function();
+  return;
+}
+)");
+}
+
 }  // namespace
 }  // namespace tint::writer::glsl
diff --git a/src/tint/writer/glsl/generator_impl_function_test.cc b/src/tint/writer/glsl/generator_impl_function_test.cc
index 56beefc..0cb72ee 100644
--- a/src/tint/writer/glsl/generator_impl_function_test.cc
+++ b/src/tint/writer/glsl/generator_impl_function_test.cc
@@ -462,13 +462,17 @@
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   int a;
   float b;
+};
+
+layout(binding = 0, std430) buffer coord_block_ssbo {
+  Data inner;
 } coord;
 
 void frag_main() {
-  float v = coord.b;
+  float v = coord.inner.b;
   return;
 }
 
@@ -506,13 +510,17 @@
               R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   int a;
   float b;
+};
+
+layout(binding = 0, std430) buffer coord_block_ssbo {
+  Data inner;
 } coord;
 
 void frag_main() {
-  float v = coord.b;
+  float v = coord.inner.b;
   return;
 }
 
@@ -547,13 +555,17 @@
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   int a;
   float b;
+};
+
+layout(binding = 0, std430) buffer coord_block_ssbo {
+  Data inner;
 } coord;
 
 void frag_main() {
-  coord.b = 2.0f;
+  coord.inner.b = 2.0f;
   return;
 }
 
@@ -588,13 +600,17 @@
     EXPECT_EQ(gen.result(), R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   int a;
   float b;
+};
+
+layout(binding = 0, std430) buffer coord_block_ssbo {
+  Data inner;
 } coord;
 
 void frag_main() {
-  coord.b = 2.0f;
+  coord.inner.b = 2.0f;
   return;
 }
 
@@ -678,12 +694,16 @@
               R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer S_ssbo {
+struct S {
   float x;
+};
+
+layout(binding = 0, std430) buffer coord_block_ssbo {
+  S inner;
 } coord;
 
 float sub_func(float param) {
-  return coord.x;
+  return coord.inner.x;
 }
 
 void frag_main() {
@@ -895,12 +915,16 @@
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   float d;
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void a() {
-  float v = data.d;
+  float v = data.inner.d;
   return;
 }
 
@@ -910,7 +934,7 @@
   return;
 }
 void b() {
-  float v = data.d;
+  float v = data.inner.d;
   return;
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
index a7392cd..a906390 100644
--- a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
@@ -179,27 +179,27 @@
 
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_MemberAccessor,
                          GlslGeneratorImplTest_MemberAccessor_StorageBufferLoad,
-                         testing::Values(TypeCase{ty_u32, "data.b"},
-                                         TypeCase{ty_f32, "data.b"},
-                                         TypeCase{ty_i32, "data.b"},
-                                         TypeCase{ty_vec2<u32>, "data.b"},
-                                         TypeCase{ty_vec2<f32>, "data.b"},
-                                         TypeCase{ty_vec2<i32>, "data.b"},
-                                         TypeCase{ty_vec3<u32>, "data.b"},
-                                         TypeCase{ty_vec3<f32>, "data.b"},
-                                         TypeCase{ty_vec3<i32>, "data.b"},
-                                         TypeCase{ty_vec4<u32>, "data.b"},
-                                         TypeCase{ty_vec4<f32>, "data.b"},
-                                         TypeCase{ty_vec4<i32>, "data.b"},
-                                         TypeCase{ty_mat2x2<f32>, "data.b"},
-                                         TypeCase{ty_mat2x3<f32>, "data.b"},
-                                         TypeCase{ty_mat2x4<f32>, "data.b"},
-                                         TypeCase{ty_mat3x2<f32>, "data.b"},
-                                         TypeCase{ty_mat3x3<f32>, "data.b"},
-                                         TypeCase{ty_mat3x4<f32>, "data.b"},
-                                         TypeCase{ty_mat4x2<f32>, "data.b"},
-                                         TypeCase{ty_mat4x3<f32>, "data.b"},
-                                         TypeCase{ty_mat4x4<f32>, "data.b"}));
+                         testing::Values(TypeCase{ty_u32, "data.inner.b"},
+                                         TypeCase{ty_f32, "data.inner.b"},
+                                         TypeCase{ty_i32, "data.inner.b"},
+                                         TypeCase{ty_vec2<u32>, "data.inner.b"},
+                                         TypeCase{ty_vec2<f32>, "data.inner.b"},
+                                         TypeCase{ty_vec2<i32>, "data.inner.b"},
+                                         TypeCase{ty_vec3<u32>, "data.inner.b"},
+                                         TypeCase{ty_vec3<f32>, "data.inner.b"},
+                                         TypeCase{ty_vec3<i32>, "data.inner.b"},
+                                         TypeCase{ty_vec4<u32>, "data.inner.b"},
+                                         TypeCase{ty_vec4<f32>, "data.inner.b"},
+                                         TypeCase{ty_vec4<i32>, "data.inner.b"},
+                                         TypeCase{ty_mat2x2<f32>, "data.inner.b"},
+                                         TypeCase{ty_mat2x3<f32>, "data.inner.b"},
+                                         TypeCase{ty_mat2x4<f32>, "data.inner.b"},
+                                         TypeCase{ty_mat3x2<f32>, "data.inner.b"},
+                                         TypeCase{ty_mat3x3<f32>, "data.inner.b"},
+                                         TypeCase{ty_mat3x4<f32>, "data.inner.b"},
+                                         TypeCase{ty_mat4x2<f32>, "data.inner.b"},
+                                         TypeCase{ty_mat4x3<f32>, "data.inner.b"},
+                                         TypeCase{ty_mat4x4<f32>, "data.inner.b"}));
 
 using GlslGeneratorImplTest_MemberAccessor_StorageBufferStore =
     GlslGeneratorImplTest_MemberAccessorWithParam<TypeCase>;
@@ -231,27 +231,27 @@
 
 INSTANTIATE_TEST_SUITE_P(GlslGeneratorImplTest_MemberAccessor,
                          GlslGeneratorImplTest_MemberAccessor_StorageBufferStore,
-                         testing::Values(TypeCase{ty_u32, "data.b = value"},
-                                         TypeCase{ty_f32, "data.b = value"},
-                                         TypeCase{ty_i32, "data.b = value"},
-                                         TypeCase{ty_vec2<u32>, "data.b = value"},
-                                         TypeCase{ty_vec2<f32>, "data.b = value"},
-                                         TypeCase{ty_vec2<i32>, "data.b = value"},
-                                         TypeCase{ty_vec3<u32>, "data.b = value"},
-                                         TypeCase{ty_vec3<f32>, "data.b = value"},
-                                         TypeCase{ty_vec3<i32>, "data.b = value"},
-                                         TypeCase{ty_vec4<u32>, "data.b = value"},
-                                         TypeCase{ty_vec4<f32>, "data.b = value"},
-                                         TypeCase{ty_vec4<i32>, "data.b = value"},
-                                         TypeCase{ty_mat2x2<f32>, "data.b = value"},
-                                         TypeCase{ty_mat2x3<f32>, "data.b = value"},
-                                         TypeCase{ty_mat2x4<f32>, "data.b = value"},
-                                         TypeCase{ty_mat3x2<f32>, "data.b = value"},
-                                         TypeCase{ty_mat3x3<f32>, "data.b = value"},
-                                         TypeCase{ty_mat3x4<f32>, "data.b = value"},
-                                         TypeCase{ty_mat4x2<f32>, "data.b = value"},
-                                         TypeCase{ty_mat4x3<f32>, "data.b = value"},
-                                         TypeCase{ty_mat4x4<f32>, "data.b = value"}));
+                         testing::Values(TypeCase{ty_u32, "data.inner.b = value"},
+                                         TypeCase{ty_f32, "data.inner.b = value"},
+                                         TypeCase{ty_i32, "data.inner.b = value"},
+                                         TypeCase{ty_vec2<u32>, "data.inner.b = value"},
+                                         TypeCase{ty_vec2<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_vec2<i32>, "data.inner.b = value"},
+                                         TypeCase{ty_vec3<u32>, "data.inner.b = value"},
+                                         TypeCase{ty_vec3<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_vec3<i32>, "data.inner.b = value"},
+                                         TypeCase{ty_vec4<u32>, "data.inner.b = value"},
+                                         TypeCase{ty_vec4<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_vec4<i32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat2x2<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat2x3<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat2x4<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat3x2<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat3x3<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat3x4<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat4x2<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat4x3<f32>, "data.inner.b = value"},
+                                         TypeCase{ty_mat4x4<f32>, "data.inner.b = value"}));
 
 TEST_F(GlslGeneratorImplTest_MemberAccessor, StorageBuffer_Store_Matrix_Empty) {
     // struct Data {
@@ -277,16 +277,20 @@
         R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   int a;
   uint pad;
   uint pad_1;
   uint pad_2;
   mat2x3 b;
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  data.b = mat2x3(vec3(0.0f), vec3(0.0f));
+  data.inner.b = mat2x3(vec3(0.0f), vec3(0.0f));
 }
 
 void main() {
@@ -321,16 +325,20 @@
         R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   float z;
   uint pad;
   uint pad_1;
   uint pad_2;
   mat4x3 a;
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  float x = data.a[2][1];
+  float x = data.inner.a[2][1];
 }
 
 void main() {
@@ -365,13 +373,17 @@
         R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   float z;
   int a[5];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  int x = data.a[2];
+  int x = data.inner.a[2];
 }
 
 void main() {
@@ -409,16 +421,20 @@
         R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   float z;
   int a[5];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
   int a = 2;
   int b = 4;
   int c = 3;
-  int x = data.a[((a + b) - c)];
+  int x = data.inner.a[((a + b) - c)];
 }
 
 void main() {
@@ -452,13 +468,17 @@
         R"(#version 310 es
 precision mediump float;
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   float z;
   int a[5];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  data.a[2] = 2;
+  data.inner.a[2] = 2;
 }
 
 void main() {
@@ -508,12 +528,16 @@
   uint pad_1;
 };
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   Inner c[4];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  vec3 x = data.c[2].b;
+  vec3 x = data.inner.c[2].b;
 }
 
 void main() {
@@ -565,12 +589,16 @@
   uint pad_1;
 };
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   Inner c[4];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  vec2 x = data.c[2].b.xy;
+  vec2 x = data.inner.c[2].b.xy;
 }
 
 void main() {
@@ -623,12 +651,16 @@
   uint pad_1;
 };
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   Inner c[4];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  float x = data.c[2].b.g;
+  float x = data.inner.c[2].b.g;
 }
 
 void main() {
@@ -680,12 +712,16 @@
   uint pad_1;
 };
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   Inner c[4];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  float x = data.c[2].b[1];
+  float x = data.inner.c[2].b[1];
 }
 
 void main() {
@@ -736,12 +772,16 @@
   uint pad_1;
 };
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   Inner c[4];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  data.c[2].b = vec3(1.0f, 2.0f, 3.0f);
+  data.inner.c[2].b = vec3(1.0f, 2.0f, 3.0f);
 }
 
 void main() {
@@ -793,12 +833,16 @@
   uint pad_1;
 };
 
-layout(binding = 0, std430) buffer Data_ssbo {
+struct Data {
   Inner c[4];
+};
+
+layout(binding = 0, std430) buffer data_block_ssbo {
+  Data inner;
 } data;
 
 void tint_symbol() {
-  data.c[2].b.y = 1.0f;
+  data.inner.c[2].b.y = 1.0f;
 }
 
 void main() {
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index d9c3479..92e0cc5 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -162,6 +162,7 @@
         polyfills.acosh = transform::BuiltinPolyfill::Level::kFull;
         polyfills.asinh = true;
         polyfills.atanh = 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`.
@@ -1047,6 +1048,9 @@
     if (type == sem::BuiltinType::kRadians) {
         return EmitRadiansCall(out, expr, builtin);
     }
+    if (type == sem::BuiltinType::kQuantizeToF16) {
+        return EmitQuantizeToF16Call(out, expr, builtin);
+    }
     if (builtin->IsDataPacking()) {
         return EmitDataPackingCall(out, expr, builtin);
     }
@@ -1940,6 +1944,22 @@
                              });
 }
 
+bool GeneratorImpl::EmitQuantizeToF16Call(std::ostream& out,
+                                          const ast::CallExpression* expr,
+                                          const sem::Builtin* builtin) {
+    // Emulate by casting to min16float and back again.
+    std::string width;
+    if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
+        width = std::to_string(vec->Width());
+    }
+    out << "float" << width << "(min16float" << width << "(";
+    if (!EmitExpression(out, expr->args[0])) {
+        return false;
+    }
+    out << "))";
+    return true;
+}
+
 bool GeneratorImpl::EmitDataPackingCall(std::ostream& out,
                                         const ast::CallExpression* expr,
                                         const sem::Builtin* builtin) {
diff --git a/src/tint/writer/hlsl/generator_impl.h b/src/tint/writer/hlsl/generator_impl.h
index 2742f00..eca7734 100644
--- a/src/tint/writer/hlsl/generator_impl.h
+++ b/src/tint/writer/hlsl/generator_impl.h
@@ -267,6 +267,14 @@
     bool EmitDataUnpackingCall(std::ostream& out,
                                const ast::CallExpression* expr,
                                const sem::Builtin* builtin);
+    /// Handles generating a call to the `quantizeToF16()` intrinsic
+    /// @param out the output of the expression stream
+    /// @param expr the call expression
+    /// @param builtin the semantic information for the builtin
+    /// @returns true if the call expression is emitted
+    bool EmitQuantizeToF16Call(std::ostream& out,
+                               const ast::CallExpression* expr,
+                               const sem::Builtin* builtin);
     /// Handles generating a call to DP4a builtins (dot4I8Packed and dot4U8Packed)
     /// @param out the output of the expression stream
     /// @param expr the call expression
diff --git a/src/tint/writer/hlsl/generator_impl_import_test.cc b/src/tint/writer/hlsl/generator_impl_import_test.cc
index e953afb..96ed7ee 100644
--- a/src/tint/writer/hlsl/generator_impl_import_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_import_test.cc
@@ -268,5 +268,31 @@
     EXPECT_EQ(out.str(), std::string("determinant(var)"));
 }
 
+TEST_F(HlslGeneratorImplTest_Import, HlslImportData_QuantizeToF16_Scalar) {
+    GlobalVar("v", Expr(2_f), ast::AddressSpace::kPrivate);
+
+    auto* expr = Call("quantizeToF16", "v");
+    WrapInFunction(expr);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    EXPECT_EQ(out.str(), std::string("float(min16float(v))"));
+}
+
+TEST_F(HlslGeneratorImplTest_Import, HlslImportData_QuantizeToF16_Vector) {
+    GlobalVar("v", vec3<f32>(2_f), ast::AddressSpace::kPrivate);
+
+    auto* expr = Call("quantizeToF16", "v");
+    WrapInFunction(expr);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    EXPECT_EQ(out.str(), std::string("float3(min16float3(v))"));
+}
+
 }  // namespace
 }  // namespace tint::writer::hlsl
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 43633b5..65b5275 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -171,6 +171,7 @@
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.bitshift_modulo = true;  // crbug.com/tint/1543
         polyfills.clamp_int = true;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
         polyfills.first_leading_bit = true;
@@ -691,6 +692,18 @@
             out << "))";
             return true;
         }
+        case sem::BuiltinType::kQuantizeToF16: {
+            std::string width = "";
+            if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
+                width = std::to_string(vec->Width());
+            }
+            out << "float" << width << "(half" << width << "(";
+            if (!EmitExpression(out, expr->args[0])) {
+                return false;
+            }
+            out << "))";
+            return true;
+        }
         // TODO(crbug.com/tint/661): Combine sequential barriers to a single
         // instruction.
         case sem::BuiltinType::kStorageBarrier: {
diff --git a/src/tint/writer/msl/generator_impl_import_test.cc b/src/tint/writer/msl/generator_impl_import_test.cc
index 4a81130..07f4acd 100644
--- a/src/tint/writer/msl/generator_impl_import_test.cc
+++ b/src/tint/writer/msl/generator_impl_import_test.cc
@@ -247,5 +247,31 @@
     EXPECT_EQ(out.str(), std::string("determinant(var)"));
 }
 
+TEST_F(MslGeneratorImplTest, MslImportData_QuantizeToF16_Scalar) {
+    GlobalVar("v", Expr(2_f), ast::AddressSpace::kPrivate);
+
+    auto* expr = Call("quantizeToF16", "v");
+    WrapInFunction(expr);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    EXPECT_EQ(out.str(), "float(half(v))");
+}
+
+TEST_F(MslGeneratorImplTest, MslImportData_QuantizeToF16_Vector) {
+    GlobalVar("v", vec3<f32>(2_f), ast::AddressSpace::kPrivate);
+
+    auto* expr = Call("quantizeToF16", "v");
+    WrapInFunction(expr);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitCall(out, expr)) << gen.error();
+    EXPECT_EQ(out.str(), "float3(half3(v))");
+}
+
 }  // namespace
 }  // namespace tint::writer::msl
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 97a29f3..6342f71 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -2505,6 +2505,9 @@
             }
             return result_id;
         }
+        case BuiltinType::kQuantizeToF16:
+            op = spv::Op::OpQuantizeToF16;
+            break;
         case BuiltinType::kReverseBits:
             op = spv::Op::OpBitReverse;
             break;
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index 3c97da7..efdc43b 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -1854,6 +1854,105 @@
     Validate(b);
 }
 
+TEST_F(BuiltinBuilderTest, Call_QuantizeToF16_Scalar) {
+    GlobalVar("v", Expr(2_f), ast::AddressSpace::kPrivate);
+
+    Func("a_func", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Let("l", Call("quantizeToF16", "v"))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+    auto got = DumpBuilder(b);
+    auto* expect = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %7 "a_func"
+OpExecutionMode %7 OriginUpperLeft
+OpName %3 "v"
+OpName %7 "a_func"
+%1 = OpTypeFloat 32
+%2 = OpConstant %1 2
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+%7 = OpFunction %6 None %5
+%8 = OpLabel
+%10 = OpLoad %1 %3
+%9 = OpQuantizeToF16 %1 %10
+OpReturn
+OpFunctionEnd
+)";
+    EXPECT_EQ(expect, got);
+
+    Validate(b);
+}
+
+TEST_F(BuiltinBuilderTest, Call_QuantizeToF16_Vector) {
+    GlobalVar("v", vec3<f32>(2_f), ast::AddressSpace::kPrivate);
+
+    Func("a_func", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(Let("l", Call("quantizeToF16", "v"))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+    auto got = DumpBuilder(b);
+    auto* expect = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %24 "a_func"
+OpExecutionMode %24 OriginUpperLeft
+OpName %5 "v"
+OpName %8 "tint_quantizeToF16"
+OpName %9 "v_1"
+OpName %24 "a_func"
+%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%7 = OpTypeFunction %1 %1
+%12 = OpTypeInt 32 0
+%13 = OpConstantNull %12
+%16 = OpConstant %12 1
+%19 = OpConstant %12 2
+%23 = OpTypeVoid
+%22 = OpTypeFunction %23
+%8 = OpFunction %1 None %7
+%9 = OpFunctionParameter %1
+%10 = OpLabel
+%14 = OpCompositeExtract %2 %9 0
+%11 = OpQuantizeToF16 %2 %14
+%17 = OpCompositeExtract %2 %9 1
+%15 = OpQuantizeToF16 %2 %17
+%20 = OpCompositeExtract %2 %9 2
+%18 = OpQuantizeToF16 %2 %20
+%21 = OpCompositeConstruct %1 %11 %15 %18
+OpReturnValue %21
+OpFunctionEnd
+%24 = OpFunction %23 None %22
+%25 = OpLabel
+%27 = OpLoad %1 %5
+%26 = OpFunctionCall %1 %8 %27
+OpReturn
+OpFunctionEnd
+)";
+    EXPECT_EQ(expect, got);
+
+    Validate(b);
+}
+
 }  // namespace float_builtin_tests
 
 // Tests for Numeric builtins with all integer parameter
@@ -3241,25 +3340,26 @@
 
     ASSERT_EQ(b.functions().size(), 1_u);
 
-    auto* expected_types = R"(%4 = OpTypeInt 32 0
-%5 = OpTypeInt 32 1
-%3 = OpTypeStruct %4 %5
+    auto* expected_types = R"(%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%4 = OpTypeStruct %5 %6
+%3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%7 = OpTypeVoid
-%6 = OpTypeFunction %7
-%11 = OpConstant %4 1
-%12 = OpConstant %4 0
-%14 = OpTypePointer StorageBuffer %4
-%18 = OpTypePointer StorageBuffer %5
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+%12 = OpConstant %5 1
+%13 = OpConstant %5 0
+%15 = OpTypePointer StorageBuffer %5
+%19 = OpTypePointer StorageBuffer %6
 )";
     auto got_types = DumpInstructions(b.types());
     EXPECT_EQ(expected_types, got_types);
 
-    auto* expected_instructions = R"(%15 = OpAccessChain %14 %1 %12
-%10 = OpAtomicLoad %4 %15 %11 %12
-%19 = OpAccessChain %18 %1 %11
-%16 = OpAtomicLoad %5 %19 %11 %12
+    auto* expected_instructions = R"(%16 = OpAccessChain %15 %1 %13 %13
+%11 = OpAtomicLoad %5 %16 %12 %13
+%20 = OpAccessChain %19 %1 %13 %12
+%17 = OpAtomicLoad %6 %20 %12 %13
 OpReturn
 )";
     auto got_instructions = DumpInstructions(b.functions()[0].instructions());
@@ -3306,34 +3406,35 @@
 
     ASSERT_EQ(b.functions().size(), 1_u);
 
-    auto* expected_types = R"(%4 = OpTypeInt 32 0
-%5 = OpTypeInt 32 1
-%3 = OpTypeStruct %4 %5
+    auto* expected_types = R"(%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%4 = OpTypeStruct %5 %6
+%3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%7 = OpTypeVoid
-%6 = OpTypeFunction %7
-%10 = OpConstant %4 1
-%12 = OpTypePointer Function %4
-%13 = OpConstantNull %4
-%14 = OpConstant %5 2
-%16 = OpTypePointer Function %5
-%17 = OpConstantNull %5
-%19 = OpConstant %4 0
-%21 = OpTypePointer StorageBuffer %4
-%26 = OpTypePointer StorageBuffer %5
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+%11 = OpConstant %5 1
+%13 = OpTypePointer Function %5
+%14 = OpConstantNull %5
+%15 = OpConstant %6 2
+%17 = OpTypePointer Function %6
+%18 = OpConstantNull %6
+%20 = OpConstant %5 0
+%22 = OpTypePointer StorageBuffer %5
+%27 = OpTypePointer StorageBuffer %6
 )";
     auto got_types = DumpInstructions(b.types());
     EXPECT_EQ(expected_types, got_types);
 
-    auto* expected_instructions = R"(OpStore %11 %10
-OpStore %15 %14
-%22 = OpAccessChain %21 %1 %19
-%23 = OpLoad %4 %11
-OpAtomicStore %22 %10 %19 %23
-%27 = OpAccessChain %26 %1 %10
-%28 = OpLoad %5 %15
-OpAtomicStore %27 %10 %19 %28
+    auto* expected_instructions = R"(OpStore %12 %11
+OpStore %16 %15
+%23 = OpAccessChain %22 %1 %20 %20
+%24 = OpLoad %5 %12
+OpAtomicStore %23 %11 %20 %24
+%28 = OpAccessChain %27 %1 %20 %11
+%29 = OpLoad %6 %16
+OpAtomicStore %28 %11 %20 %29
 OpReturn
 )";
     auto got_instructions = DumpInstructions(b.functions()[0].instructions());
@@ -3376,28 +3477,29 @@
 
     ASSERT_EQ(b.functions().size(), 1_u);
 
-    std::string expected_types = R"(%4 = OpTypeInt 32 1
+    std::string expected_types = R"(%5 = OpTypeInt 32 1
+%4 = OpTypeStruct %5
 %3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
-%9 = OpConstant %4 10
-%11 = OpTypePointer Function %4
-%12 = OpConstantNull %4
-%14 = OpTypeInt 32 0
-%15 = OpConstant %14 1
-%16 = OpConstant %14 0
-%18 = OpTypePointer StorageBuffer %4
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
+%10 = OpConstant %5 10
+%12 = OpTypePointer Function %5
+%13 = OpConstantNull %5
+%15 = OpTypeInt 32 0
+%16 = OpConstant %15 1
+%17 = OpConstant %15 0
+%19 = OpTypePointer StorageBuffer %5
 )";
     auto got_types = DumpInstructions(b.types());
     EXPECT_EQ(expected_types, got_types);
 
-    std::string expected_instructions = R"(OpStore %10 %9
-%19 = OpAccessChain %18 %1 %16
-%20 = OpLoad %4 %10
+    std::string expected_instructions = R"(OpStore %11 %10
+%20 = OpAccessChain %19 %1 %17 %17
+%21 = OpLoad %5 %11
 )";
-    expected_instructions += "%13 = " + GetParam().op + " %4 %19 %15 %16 %20\n";
+    expected_instructions += "%14 = " + GetParam().op + " %5 %20 %16 %17 %21\n";
     expected_instructions += "OpReturn\n";
 
     auto got_instructions = DumpInstructions(b.functions()[0].instructions());
@@ -3448,27 +3550,28 @@
 
     ASSERT_EQ(b.functions().size(), 1_u);
 
-    std::string expected_types = R"(%4 = OpTypeInt 32 0
+    std::string expected_types = R"(%5 = OpTypeInt 32 0
+%4 = OpTypeStruct %5
 %3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
-%9 = OpConstant %4 10
-%11 = OpTypePointer Function %4
-%12 = OpConstantNull %4
-%14 = OpConstant %4 1
-%15 = OpConstant %4 0
-%17 = OpTypePointer StorageBuffer %4
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
+%10 = OpConstant %5 10
+%12 = OpTypePointer Function %5
+%13 = OpConstantNull %5
+%15 = OpConstant %5 1
+%16 = OpConstant %5 0
+%18 = OpTypePointer StorageBuffer %5
 )";
     auto got_types = DumpInstructions(b.types());
     EXPECT_EQ(expected_types, got_types);
 
-    std::string expected_instructions = R"(OpStore %10 %9
-%18 = OpAccessChain %17 %1 %15
-%19 = OpLoad %4 %10
+    std::string expected_instructions = R"(OpStore %11 %10
+%19 = OpAccessChain %18 %1 %16 %16
+%20 = OpLoad %5 %11
 )";
-    expected_instructions += "%13 = " + GetParam().op + " %4 %18 %14 %15 %19\n";
+    expected_instructions += "%14 = " + GetParam().op + " %5 %19 %15 %16 %20\n";
     expected_instructions += "OpReturn\n";
 
     auto got_instructions = DumpInstructions(b.functions()[0].instructions());
@@ -3525,35 +3628,36 @@
 
     ASSERT_EQ(b.functions().size(), 1_u);
 
-    auto* expected_types = R"(%4 = OpTypeInt 32 0
-%5 = OpTypeInt 32 1
-%3 = OpTypeStruct %4 %5
+    auto* expected_types = R"(%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%4 = OpTypeStruct %5 %6
+%3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%7 = OpTypeVoid
-%6 = OpTypeFunction %7
-%10 = OpConstant %4 10
-%12 = OpTypePointer Function %4
-%13 = OpConstantNull %4
-%14 = OpConstant %5 10
-%16 = OpTypePointer Function %5
-%17 = OpConstantNull %5
-%19 = OpConstant %4 1
-%20 = OpConstant %4 0
-%22 = OpTypePointer StorageBuffer %4
-%27 = OpTypePointer StorageBuffer %5
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+%11 = OpConstant %5 10
+%13 = OpTypePointer Function %5
+%14 = OpConstantNull %5
+%15 = OpConstant %6 10
+%17 = OpTypePointer Function %6
+%18 = OpConstantNull %6
+%20 = OpConstant %5 1
+%21 = OpConstant %5 0
+%23 = OpTypePointer StorageBuffer %5
+%28 = OpTypePointer StorageBuffer %6
 )";
     auto got_types = DumpInstructions(b.types());
     EXPECT_EQ(expected_types, got_types);
 
-    auto* expected_instructions = R"(OpStore %11 %10
-OpStore %15 %14
-%23 = OpAccessChain %22 %1 %20
-%24 = OpLoad %4 %11
-%18 = OpAtomicExchange %4 %23 %19 %20 %24
-%28 = OpAccessChain %27 %1 %19
-%29 = OpLoad %5 %15
-%25 = OpAtomicExchange %5 %28 %19 %20 %29
+    auto* expected_instructions = R"(OpStore %12 %11
+OpStore %16 %15
+%24 = OpAccessChain %23 %1 %21 %21
+%25 = OpLoad %5 %12
+%19 = OpAtomicExchange %5 %24 %20 %21 %25
+%29 = OpAccessChain %28 %1 %21 %20
+%30 = OpLoad %6 %16
+%26 = OpAtomicExchange %6 %29 %20 %21 %30
 OpReturn
 )";
     auto got_instructions = DumpInstructions(b.functions()[0].instructions());
@@ -3598,36 +3702,37 @@
 
     ASSERT_EQ(b.functions().size(), 1_u);
 
-    auto* expected_types = R"(%4 = OpTypeInt 32 0
-%5 = OpTypeInt 32 1
-%3 = OpTypeStruct %4 %5
+    auto* expected_types = R"(%5 = OpTypeInt 32 0
+%6 = OpTypeInt 32 1
+%4 = OpTypeStruct %5 %6
+%3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%7 = OpTypeVoid
-%6 = OpTypeFunction %7
-%12 = OpTypeBool
-%11 = OpTypeStruct %4 %12
-%13 = OpConstant %4 1
-%14 = OpConstant %4 0
-%16 = OpTypePointer StorageBuffer %4
-%18 = OpConstant %4 20
-%19 = OpConstant %4 10
-%23 = OpTypeStruct %5 %12
-%25 = OpTypePointer StorageBuffer %5
-%27 = OpConstant %5 20
-%28 = OpConstant %5 10
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+%13 = OpTypeBool
+%12 = OpTypeStruct %5 %13
+%14 = OpConstant %5 1
+%15 = OpConstant %5 0
+%17 = OpTypePointer StorageBuffer %5
+%19 = OpConstant %5 20
+%20 = OpConstant %5 10
+%24 = OpTypeStruct %6 %13
+%26 = OpTypePointer StorageBuffer %6
+%28 = OpConstant %6 20
+%29 = OpConstant %6 10
 )";
     auto got_types = DumpInstructions(b.types());
     EXPECT_EQ(expected_types, got_types);
 
-    auto* expected_instructions = R"(%17 = OpAccessChain %16 %1 %14
-%20 = OpAtomicCompareExchange %4 %17 %13 %14 %14 %18 %19
-%21 = OpIEqual %12 %20 %19
-%10 = OpCompositeConstruct %11 %20 %21
-%26 = OpAccessChain %25 %1 %13
-%29 = OpAtomicCompareExchange %5 %26 %13 %14 %14 %27 %28
-%30 = OpIEqual %12 %29 %28
-%22 = OpCompositeConstruct %23 %29 %30
+    auto* expected_instructions = R"(%18 = OpAccessChain %17 %1 %15 %15
+%21 = OpAtomicCompareExchange %5 %18 %14 %15 %15 %19 %20
+%22 = OpIEqual %13 %21 %20
+%11 = OpCompositeConstruct %12 %21 %22
+%27 = OpAccessChain %26 %1 %15 %14
+%30 = OpAtomicCompareExchange %6 %27 %14 %15 %15 %28 %29
+%31 = OpIEqual %13 %30 %29
+%23 = OpCompositeConstruct %24 %30 %31
 OpReturn
 )";
     auto got_instructions = DumpInstructions(b.functions()[0].instructions());
diff --git a/src/tint/writer/spirv/builder_function_test.cc b/src/tint/writer/spirv/builder_function_test.cc
index b28817c..efaefed 100644
--- a/src/tint/writer/spirv/builder_function_test.cc
+++ b/src/tint/writer/spirv/builder_function_test.cc
@@ -228,46 +228,50 @@
     ASSERT_TRUE(b.Build());
     EXPECT_EQ(DumpBuilder(b), R"(OpCapability Shader
 OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %7 "a"
-OpEntryPoint GLCompute %17 "b"
-OpExecutionMode %7 LocalSize 1 1 1
-OpExecutionMode %17 LocalSize 1 1 1
-OpName %3 "Data"
-OpMemberName %3 0 "d"
+OpEntryPoint GLCompute %8 "a"
+OpEntryPoint GLCompute %18 "b"
+OpExecutionMode %8 LocalSize 1 1 1
+OpExecutionMode %18 LocalSize 1 1 1
+OpName %3 "data_block"
+OpMemberName %3 0 "inner"
+OpName %4 "Data"
+OpMemberName %4 0 "d"
 OpName %1 "data"
-OpName %7 "a"
-OpName %14 "v"
-OpName %17 "b"
-OpName %21 "v"
+OpName %8 "a"
+OpName %15 "v"
+OpName %18 "b"
+OpName %22 "v"
 OpDecorate %3 Block
 OpMemberDecorate %3 0 Offset 0
+OpMemberDecorate %4 0 Offset 0
 OpDecorate %1 Binding 0
 OpDecorate %1 DescriptorSet 0
-%4 = OpTypeFloat 32
+%5 = OpTypeFloat 32
+%4 = OpTypeStruct %5
 %3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
-%9 = OpTypeInt 32 0
-%10 = OpConstant %9 0
-%11 = OpTypePointer StorageBuffer %4
-%15 = OpTypePointer Function %4
-%16 = OpConstantNull %4
-%7 = OpFunction %6 None %5
-%8 = OpLabel
-%14 = OpVariable %15 Function %16
-%12 = OpAccessChain %11 %1 %10
-%13 = OpLoad %4 %12
-OpStore %14 %13
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 0
+%12 = OpTypePointer StorageBuffer %5
+%16 = OpTypePointer Function %5
+%17 = OpConstantNull %5
+%8 = OpFunction %7 None %6
+%9 = OpLabel
+%15 = OpVariable %16 Function %17
+%13 = OpAccessChain %12 %1 %11 %11
+%14 = OpLoad %5 %13
+OpStore %15 %14
 OpReturn
 OpFunctionEnd
-%17 = OpFunction %6 None %5
-%18 = OpLabel
-%21 = OpVariable %15 Function %16
-%19 = OpAccessChain %11 %1 %10
-%20 = OpLoad %4 %19
-OpStore %21 %20
+%18 = OpFunction %7 None %6
+%19 = OpLabel
+%22 = OpVariable %16 Function %17
+%20 = OpAccessChain %12 %1 %11 %11
+%21 = OpLoad %5 %20
+OpStore %22 %21
 OpReturn
 OpFunctionEnd
 )");
diff --git a/src/tint/writer/spirv/builder_global_variable_test.cc b/src/tint/writer/spirv/builder_global_variable_test.cc
index 64eac22..7d3ec59 100644
--- a/src/tint/writer/spirv/builder_global_variable_test.cc
+++ b/src/tint/writer/spirv/builder_global_variable_test.cc
@@ -316,23 +316,27 @@
 
     EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
 OpMemberDecorate %3 0 Offset 0
-OpMemberDecorate %3 1 Offset 4
+OpMemberDecorate %4 0 Offset 0
+OpMemberDecorate %4 1 Offset 4
 OpDecorate %1 NonWritable
 OpDecorate %1 Binding 0
 OpDecorate %1 DescriptorSet 0
 )");
-    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
-OpMemberName %3 0 "a"
-OpMemberName %3 1 "b"
+    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "b_block"
+OpMemberName %3 0 "inner"
+OpName %4 "A"
+OpMemberName %4 0 "a"
+OpMemberName %4 1 "b"
 OpName %1 "b"
-OpName %7 "unused_entry_point"
+OpName %8 "unused_entry_point"
 )");
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
-%3 = OpTypeStruct %4 %4
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeInt 32 1
+%4 = OpTypeStruct %5 %5
+%3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
 }
 
@@ -354,21 +358,25 @@
 
     EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
 OpMemberDecorate %3 0 Offset 0
+OpMemberDecorate %4 0 Offset 0
 OpDecorate %1 NonWritable
 OpDecorate %1 Binding 0
 OpDecorate %1 DescriptorSet 0
 )");
-    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
-OpMemberName %3 0 "a"
+    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "b_block"
+OpMemberName %3 0 "inner"
+OpName %4 "A"
+OpMemberName %4 0 "a"
 OpName %1 "b"
-OpName %7 "unused_entry_point"
+OpName %8 "unused_entry_point"
 )");
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeInt 32 1
+%4 = OpTypeStruct %5
 %3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
 }
 
@@ -390,21 +398,25 @@
 
     EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
 OpMemberDecorate %3 0 Offset 0
+OpMemberDecorate %4 0 Offset 0
 OpDecorate %1 NonWritable
 OpDecorate %1 Binding 0
 OpDecorate %1 DescriptorSet 0
 )");
-    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
-OpMemberName %3 0 "a"
+    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "b_block"
+OpMemberName %3 0 "inner"
+OpName %4 "A"
+OpMemberName %4 0 "a"
 OpName %1 "b"
-OpName %7 "unused_entry_point"
+OpName %8 "unused_entry_point"
 )");
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeInt 32 1
+%4 = OpTypeStruct %5
 %3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
 }
 
@@ -428,25 +440,29 @@
     EXPECT_EQ(DumpInstructions(b.annots()),
               R"(OpDecorate %3 Block
 OpMemberDecorate %3 0 Offset 0
+OpMemberDecorate %4 0 Offset 0
 OpDecorate %1 NonWritable
 OpDecorate %1 DescriptorSet 0
 OpDecorate %1 Binding 0
-OpDecorate %5 DescriptorSet 1
-OpDecorate %5 Binding 0
+OpDecorate %6 DescriptorSet 1
+OpDecorate %6 Binding 0
 )");
-    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
-OpMemberName %3 0 "a"
+    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "b_block"
+OpMemberName %3 0 "inner"
+OpName %4 "A"
+OpMemberName %4 0 "a"
 OpName %1 "b"
-OpName %5 "c"
-OpName %8 "unused_entry_point"
+OpName %6 "c"
+OpName %9 "unused_entry_point"
 )");
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeInt 32 1
+%4 = OpTypeStruct %5
 %3 = OpTypeStruct %4
 %2 = OpTypePointer StorageBuffer %3
 %1 = OpVariable %2 StorageBuffer
-%5 = OpVariable %2 StorageBuffer
-%7 = OpTypeVoid
-%6 = OpTypeFunction %7
+%6 = OpVariable %2 StorageBuffer
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
 )");
 }
 
diff --git a/src/tint/writer/spirv/generator_impl.cc b/src/tint/writer/spirv/generator_impl.cc
index f75f1f2..aa5d449 100644
--- a/src/tint/writer/spirv/generator_impl.cc
+++ b/src/tint/writer/spirv/generator_impl.cc
@@ -52,6 +52,7 @@
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.bitshift_modulo = true;
         polyfills.clamp_int = true;
         polyfills.count_leading_zeros = true;
         polyfills.count_trailing_zeros = true;
@@ -61,6 +62,7 @@
         polyfills.insert_bits = transform::BuiltinPolyfill::Level::kClampParameters;
         polyfills.saturate = true;
         polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
+        polyfills.quantize_to_vec_f16 = true;  // crbug.com/tint/1741
         data.Add<transform::BuiltinPolyfill::Config>(polyfills);
         manager.Add<transform::BuiltinPolyfill>();
     }
diff --git a/tint_overrides_with_defaults.gni b/tint_overrides_with_defaults.gni
index 9680e35..9e39248 100644
--- a/tint_overrides_with_defaults.gni
+++ b/tint_overrides_with_defaults.gni
@@ -71,4 +71,9 @@
   if (!defined(tint_build_glsl_writer)) {
     tint_build_glsl_writer = true
   }
+
+  # Build unittests
+  if (!defined(tint_build_unittests)) {
+    tint_build_unittests = true
+  }
 }